1use super::{state::DocState, txn::Transaction};
2use crate::sync::Mutex;
3use crate::{
4 container::{
5 idx::ContainerIdx,
6 list::list_op::{DeleteSpan, DeleteSpanWithId, ListOp},
7 richtext::{richtext_state::PosType, RichtextState, StyleOp, TextStyleInfoFlag},
8 },
9 cursor::{Cursor, Side},
10 delta::{DeltaItem, Meta, StyleMeta, TreeExternalDiff},
11 diff::{diff, diff_impl::UpdateTimeoutError, OperateProxy},
12 event::{Diff, TextDiff, TextDiffItem, TextMeta},
13 op::ListSlice,
14 state::{IndexType, State, TreeParentId},
15 txn::EventHint,
16 utils::{string_slice::StringSlice, utf16::count_utf16_len},
17 LoroDoc, LoroDocInner,
18};
19use append_only_bytes::BytesSlice;
20use enum_as_inner::EnumAsInner;
21use generic_btree::rle::HasLength;
22use loro_common::{
23 ContainerID, ContainerType, IdFull, InternalString, LoroError, LoroResult, LoroValue, PeerID,
24 TreeID, ID,
25};
26use rustc_hash::FxHashMap;
27use serde::{Deserialize, Serialize};
28use std::{borrow::Cow, cmp::Reverse, collections::BinaryHeap, fmt::Debug, ops::Deref, sync::Arc};
29use tracing::{error, instrument};
30
31pub use crate::diff::diff_impl::UpdateOptions;
32pub use tree::TreeHandler;
33mod movable_list_apply_delta;
34mod tree;
35
36const INSERT_CONTAINER_VALUE_ARG_ERROR: &str =
37 "Cannot insert a LoroValue::Container directly. To create child container, use insert_container";
38
39mod text_update;
40
41pub trait HandlerTrait: Clone + Sized {
42 fn is_attached(&self) -> bool;
43 fn attached_handler(&self) -> Option<&BasicHandler>;
44 fn get_value(&self) -> LoroValue;
45 fn get_deep_value(&self) -> LoroValue;
46 fn kind(&self) -> ContainerType;
47 fn to_handler(&self) -> Handler;
48 fn from_handler(h: Handler) -> Option<Self>;
49 fn doc(&self) -> Option<LoroDoc>;
50 fn attach(
52 &self,
53 txn: &mut Transaction,
54 parent: &BasicHandler,
55 self_id: ContainerID,
56 ) -> LoroResult<Self>;
57 fn get_attached(&self) -> Option<Self>;
59
60 fn parent(&self) -> Option<Handler> {
61 self.attached_handler().and_then(|x| x.parent())
62 }
63
64 fn idx(&self) -> ContainerIdx {
65 self.attached_handler()
66 .map(|x| x.container_idx)
67 .unwrap_or_else(|| ContainerIdx::from_index_and_type(u32::MAX, self.kind()))
68 }
69
70 fn id(&self) -> ContainerID {
71 self.attached_handler()
72 .map(|x| x.id.clone())
73 .unwrap_or_else(|| ContainerID::new_normal(ID::NONE_ID, self.kind()))
74 }
75
76 fn with_state<R>(&self, f: impl FnOnce(&mut State) -> LoroResult<R>) -> LoroResult<R> {
77 let inner = self
78 .attached_handler()
79 .ok_or(LoroError::MisuseDetachedContainer {
80 method: "with_state",
81 })?;
82 let state = inner.doc.state.clone();
83 let mut guard = state.lock().unwrap();
84 guard.with_state_mut(inner.container_idx, f)
85 }
86}
87
88fn create_handler(inner: &BasicHandler, id: ContainerID) -> Handler {
89 Handler::new_attached(id, inner.doc.clone())
90}
91
92#[derive(Clone, Debug)]
94pub struct BasicHandler {
95 id: ContainerID,
96 container_idx: ContainerIdx,
97 doc: LoroDoc,
98}
99
100struct DetachedInner<T> {
101 value: T,
102 attached: Option<BasicHandler>,
104}
105
106impl<T> DetachedInner<T> {
107 fn new(v: T) -> Self {
108 Self {
109 value: v,
110 attached: None,
111 }
112 }
113}
114
115enum MaybeDetached<T> {
116 Detached(Arc<Mutex<DetachedInner<T>>>),
117 Attached(BasicHandler),
118}
119
120impl<T> Clone for MaybeDetached<T> {
121 fn clone(&self) -> Self {
122 match self {
123 MaybeDetached::Detached(a) => MaybeDetached::Detached(Arc::clone(a)),
124 MaybeDetached::Attached(a) => MaybeDetached::Attached(a.clone()),
125 }
126 }
127}
128
129impl<T> MaybeDetached<T> {
130 fn new_detached(v: T) -> Self {
131 MaybeDetached::Detached(Arc::new(Mutex::new(DetachedInner::new(v))))
132 }
133
134 fn is_attached(&self) -> bool {
135 match self {
136 MaybeDetached::Detached(_) => false,
137 MaybeDetached::Attached(_) => true,
138 }
139 }
140
141 fn attached_handler(&self) -> Option<&BasicHandler> {
142 match self {
143 MaybeDetached::Detached(_) => None,
144 MaybeDetached::Attached(a) => Some(a),
145 }
146 }
147
148 fn try_attached_state(&self) -> LoroResult<&BasicHandler> {
149 match self {
150 MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
151 method: "inner_state",
152 }),
153 MaybeDetached::Attached(a) => Ok(a),
154 }
155 }
156}
157
158impl<T> From<BasicHandler> for MaybeDetached<T> {
159 fn from(a: BasicHandler) -> Self {
160 MaybeDetached::Attached(a)
161 }
162}
163
164impl BasicHandler {
165 pub(crate) fn doc(&self) -> LoroDoc {
166 self.doc.clone()
167 }
168
169 #[inline]
170 fn with_doc_state<R>(&self, f: impl FnOnce(&mut DocState) -> R) -> R {
171 let state = self.doc.state.clone();
172 let mut guard = state.lock().unwrap();
173 f(&mut guard)
174 }
175
176 fn with_txn<R>(
177 &self,
178 f: impl FnOnce(&mut Transaction) -> Result<R, LoroError>,
179 ) -> Result<R, LoroError> {
180 with_txn(&self.doc, f)
181 }
182
183 fn get_parent(&self) -> Option<Handler> {
184 let parent_idx = self.doc.arena.get_parent(self.container_idx)?;
185 let parent_id = self.doc.arena.get_container_id(parent_idx).unwrap();
186 {
187 let kind = parent_id.container_type();
188 let handler = BasicHandler {
189 container_idx: parent_idx,
190 id: parent_id,
191 doc: self.doc.clone(),
192 };
193
194 Some(match kind {
195 ContainerType::Map => Handler::Map(MapHandler {
196 inner: handler.into(),
197 }),
198 ContainerType::List => Handler::List(ListHandler {
199 inner: handler.into(),
200 }),
201 ContainerType::Tree => Handler::Tree(TreeHandler {
202 inner: handler.into(),
203 }),
204 ContainerType::Text => Handler::Text(TextHandler {
205 inner: handler.into(),
206 }),
207 ContainerType::MovableList => Handler::MovableList(MovableListHandler {
208 inner: handler.into(),
209 }),
210 #[cfg(feature = "counter")]
211 ContainerType::Counter => Handler::Counter(counter::CounterHandler {
212 inner: handler.into(),
213 }),
214 ContainerType::Unknown(_) => unreachable!(),
215 })
216 }
217 }
218
219 pub fn get_value(&self) -> LoroValue {
220 self.doc
221 .state
222 .lock()
223 .unwrap()
224 .get_value_by_idx(self.container_idx)
225 }
226
227 pub fn get_deep_value(&self) -> LoroValue {
228 self.doc
229 .state
230 .lock()
231 .unwrap()
232 .get_container_deep_value(self.container_idx)
233 }
234
235 fn with_state<R>(&self, f: impl FnOnce(&mut State) -> R) -> R {
236 let mut guard = self.doc.state.lock().unwrap();
237 guard.with_state_mut(self.container_idx, f)
238 }
239
240 pub fn parent(&self) -> Option<Handler> {
241 self.get_parent()
242 }
243
244 fn is_deleted(&self) -> bool {
245 self.doc
246 .state
247 .lock()
248 .unwrap()
249 .is_deleted(self.container_idx)
250 }
251}
252
253#[derive(Clone)]
255pub struct TextHandler {
256 inner: MaybeDetached<RichtextState>,
257}
258
259impl HandlerTrait for TextHandler {
260 fn to_handler(&self) -> Handler {
261 Handler::Text(self.clone())
262 }
263
264 fn attach(
265 &self,
266 txn: &mut Transaction,
267 parent: &BasicHandler,
268 self_id: ContainerID,
269 ) -> LoroResult<Self> {
270 match &self.inner {
271 MaybeDetached::Detached(t) => {
272 let mut t = t.lock().unwrap();
273 let inner = create_handler(parent, self_id);
274 let text = inner.into_text().unwrap();
275 let mut delta: Vec<TextDelta> = Vec::new();
276 for span in t.value.iter() {
277 delta.push(TextDelta::Insert {
278 insert: span.text.to_string(),
279 attributes: span.attributes.to_option_map(),
280 });
281 }
282
283 text.apply_delta_with_txn(txn, &delta)?;
284 t.attached = text.attached_handler().cloned();
285 Ok(text)
286 }
287 MaybeDetached::Attached(a) => {
288 let new_inner = create_handler(a, self_id);
289 let ans = new_inner.into_text().unwrap();
290
291 let delta = self.get_delta();
292 ans.apply_delta_with_txn(txn, &delta)?;
293 Ok(ans)
294 }
295 }
296 }
297
298 fn attached_handler(&self) -> Option<&BasicHandler> {
299 self.inner.attached_handler()
300 }
301
302 fn get_value(&self) -> LoroValue {
303 match &self.inner {
304 MaybeDetached::Detached(t) => {
305 let t = t.lock().unwrap();
306 LoroValue::String((t.value.to_string()).into())
307 }
308 MaybeDetached::Attached(a) => a.get_value(),
309 }
310 }
311
312 fn get_deep_value(&self) -> LoroValue {
313 self.get_value()
314 }
315
316 fn is_attached(&self) -> bool {
317 matches!(&self.inner, MaybeDetached::Attached(..))
318 }
319
320 fn kind(&self) -> ContainerType {
321 ContainerType::Text
322 }
323
324 fn get_attached(&self) -> Option<Self> {
325 match &self.inner {
326 MaybeDetached::Detached(d) => d.lock().unwrap().attached.clone().map(|x| Self {
327 inner: MaybeDetached::Attached(x),
328 }),
329 MaybeDetached::Attached(_a) => Some(self.clone()),
330 }
331 }
332
333 fn from_handler(h: Handler) -> Option<Self> {
334 match h {
335 Handler::Text(x) => Some(x),
336 _ => None,
337 }
338 }
339
340 fn doc(&self) -> Option<LoroDoc> {
341 match &self.inner {
342 MaybeDetached::Detached(_) => None,
343 MaybeDetached::Attached(a) => Some(a.doc()),
344 }
345 }
346}
347
348impl std::fmt::Debug for TextHandler {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 match &self.inner {
351 MaybeDetached::Detached(_) => {
352 write!(f, "TextHandler(Unattached)")
353 }
354 MaybeDetached::Attached(a) => {
355 write!(f, "TextHandler({:?})", &a.id)
356 }
357 }
358 }
359}
360
361#[derive(Debug, Clone, EnumAsInner, Deserialize, Serialize, PartialEq)]
362#[serde(untagged)]
363pub enum TextDelta {
364 Retain {
365 retain: usize,
366 attributes: Option<FxHashMap<String, LoroValue>>,
367 },
368 Insert {
369 insert: String,
370 attributes: Option<FxHashMap<String, LoroValue>>,
371 },
372 Delete {
373 delete: usize,
374 },
375}
376
377impl TextDelta {
378 pub fn from_text_diff<'a>(diff: impl Iterator<Item = &'a TextDiffItem>) -> Vec<TextDelta> {
379 let mut ans = Vec::with_capacity(diff.size_hint().0);
380 for iter in diff {
381 match iter {
382 loro_delta::DeltaItem::Retain { len, attr } => {
383 ans.push(TextDelta::Retain {
384 retain: *len,
385 attributes: if attr.0.is_empty() {
386 None
387 } else {
388 Some(attr.0.clone())
389 },
390 });
391 }
392 loro_delta::DeltaItem::Replace {
393 value,
394 attr,
395 delete,
396 } => {
397 if value.rle_len() > 0 {
398 ans.push(TextDelta::Insert {
399 insert: value.to_string(),
400 attributes: if attr.0.is_empty() {
401 None
402 } else {
403 Some(attr.0.clone())
404 },
405 });
406 }
407 if *delete > 0 {
408 ans.push(TextDelta::Delete { delete: *delete });
409 }
410 }
411 }
412 }
413
414 ans
415 }
416
417 pub fn into_text_diff(vec: impl Iterator<Item = Self>) -> TextDiff {
418 let mut delta = TextDiff::new();
419 for item in vec {
420 match item {
421 TextDelta::Retain { retain, attributes } => {
422 delta.push_retain(retain, TextMeta(attributes.unwrap_or_default().clone()));
423 }
424 TextDelta::Insert { insert, attributes } => {
425 delta.push_insert(
426 StringSlice::from(insert.as_str()),
427 TextMeta(attributes.unwrap_or_default()),
428 );
429 }
430 TextDelta::Delete { delete } => {
431 delta.push_delete(delete);
432 }
433 }
434 }
435
436 delta
437 }
438}
439
440impl From<&DeltaItem<StringSlice, StyleMeta>> for TextDelta {
441 fn from(value: &DeltaItem<StringSlice, StyleMeta>) -> Self {
442 match value {
443 crate::delta::DeltaItem::Retain { retain, attributes } => TextDelta::Retain {
444 retain: *retain,
445 attributes: attributes.to_option_map(),
446 },
447 crate::delta::DeltaItem::Insert { insert, attributes } => TextDelta::Insert {
448 insert: insert.to_string(),
449 attributes: attributes.to_option_map(),
450 },
451 crate::delta::DeltaItem::Delete {
452 delete,
453 attributes: _,
454 } => TextDelta::Delete { delete: *delete },
455 }
456 }
457}
458
459#[derive(Clone)]
460pub struct MapHandler {
461 inner: MaybeDetached<FxHashMap<String, ValueOrHandler>>,
462}
463
464impl HandlerTrait for MapHandler {
465 fn is_attached(&self) -> bool {
466 matches!(&self.inner, MaybeDetached::Attached(..))
467 }
468
469 fn attached_handler(&self) -> Option<&BasicHandler> {
470 match &self.inner {
471 MaybeDetached::Detached(_) => None,
472 MaybeDetached::Attached(a) => Some(a),
473 }
474 }
475
476 fn get_value(&self) -> LoroValue {
477 match &self.inner {
478 MaybeDetached::Detached(m) => {
479 let m = m.lock().unwrap();
480 let mut map = FxHashMap::default();
481 for (k, v) in m.value.iter() {
482 map.insert(k.to_string(), v.to_value());
483 }
484 LoroValue::Map(map.into())
485 }
486 MaybeDetached::Attached(a) => a.get_value(),
487 }
488 }
489
490 fn get_deep_value(&self) -> LoroValue {
491 match &self.inner {
492 MaybeDetached::Detached(m) => {
493 let m = m.lock().unwrap();
494 let mut map = FxHashMap::default();
495 for (k, v) in m.value.iter() {
496 map.insert(k.to_string(), v.to_deep_value());
497 }
498 LoroValue::Map(map.into())
499 }
500 MaybeDetached::Attached(a) => a.get_deep_value(),
501 }
502 }
503
504 fn kind(&self) -> ContainerType {
505 ContainerType::Map
506 }
507
508 fn to_handler(&self) -> Handler {
509 Handler::Map(self.clone())
510 }
511
512 fn attach(
513 &self,
514 txn: &mut Transaction,
515 parent: &BasicHandler,
516 self_id: ContainerID,
517 ) -> LoroResult<Self> {
518 match &self.inner {
519 MaybeDetached::Detached(m) => {
520 let mut m = m.lock().unwrap();
521 let inner = create_handler(parent, self_id);
522 let map = inner.into_map().unwrap();
523 for (k, v) in m.value.iter() {
524 match v {
525 ValueOrHandler::Value(v) => {
526 map.insert_with_txn(txn, k, v.clone())?;
527 }
528 ValueOrHandler::Handler(h) => {
529 map.insert_container_with_txn(txn, k, h.clone())?;
530 }
531 }
532 }
533 m.attached = map.attached_handler().cloned();
534 Ok(map)
535 }
536 MaybeDetached::Attached(a) => {
537 let new_inner = create_handler(a, self_id);
538 let ans = new_inner.into_map().unwrap();
539
540 for (k, v) in self.get_value().into_map().unwrap().iter() {
541 if let LoroValue::Container(id) = v {
542 ans.insert_container_with_txn(txn, k, create_handler(a, id.clone()))?;
543 } else {
544 ans.insert_with_txn(txn, k, v.clone())?;
545 }
546 }
547
548 Ok(ans)
549 }
550 }
551 }
552
553 fn get_attached(&self) -> Option<Self> {
554 match &self.inner {
555 MaybeDetached::Detached(d) => d.lock().unwrap().attached.clone().map(|x| Self {
556 inner: MaybeDetached::Attached(x),
557 }),
558 MaybeDetached::Attached(_a) => Some(self.clone()),
559 }
560 }
561
562 fn from_handler(h: Handler) -> Option<Self> {
563 match h {
564 Handler::Map(x) => Some(x),
565 _ => None,
566 }
567 }
568
569 fn doc(&self) -> Option<LoroDoc> {
570 match &self.inner {
571 MaybeDetached::Detached(_) => None,
572 MaybeDetached::Attached(a) => Some(a.doc()),
573 }
574 }
575}
576
577impl std::fmt::Debug for MapHandler {
578 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579 match &self.inner {
580 MaybeDetached::Detached(_) => write!(f, "MapHandler Detached"),
581 MaybeDetached::Attached(a) => write!(f, "MapHandler {}", a.id),
582 }
583 }
584}
585
586#[derive(Clone)]
587pub struct ListHandler {
588 inner: MaybeDetached<Vec<ValueOrHandler>>,
589}
590
591#[derive(Clone)]
592pub struct MovableListHandler {
593 inner: MaybeDetached<Vec<ValueOrHandler>>,
594}
595
596impl HandlerTrait for MovableListHandler {
597 fn is_attached(&self) -> bool {
598 matches!(&self.inner, MaybeDetached::Attached(..))
599 }
600
601 fn attached_handler(&self) -> Option<&BasicHandler> {
602 match &self.inner {
603 MaybeDetached::Detached(_) => None,
604 MaybeDetached::Attached(a) => Some(a),
605 }
606 }
607
608 fn get_value(&self) -> LoroValue {
609 match &self.inner {
610 MaybeDetached::Detached(a) => {
611 let a = a.lock().unwrap();
612 LoroValue::List(a.value.iter().map(|v| v.to_value()).collect())
613 }
614 MaybeDetached::Attached(a) => a.get_value(),
615 }
616 }
617
618 fn get_deep_value(&self) -> LoroValue {
619 match &self.inner {
620 MaybeDetached::Detached(a) => {
621 let a = a.lock().unwrap();
622 LoroValue::List(a.value.iter().map(|v| v.to_deep_value()).collect())
623 }
624 MaybeDetached::Attached(a) => a.get_deep_value(),
625 }
626 }
627
628 fn kind(&self) -> ContainerType {
629 ContainerType::MovableList
630 }
631
632 fn to_handler(&self) -> Handler {
633 Handler::MovableList(self.clone())
634 }
635
636 fn from_handler(h: Handler) -> Option<Self> {
637 match h {
638 Handler::MovableList(x) => Some(x),
639 _ => None,
640 }
641 }
642
643 fn attach(
644 &self,
645 txn: &mut Transaction,
646 parent: &BasicHandler,
647 self_id: ContainerID,
648 ) -> LoroResult<Self> {
649 match &self.inner {
650 MaybeDetached::Detached(l) => {
651 let mut l = l.lock().unwrap();
652 let inner = create_handler(parent, self_id);
653 let list = inner.into_movable_list().unwrap();
654 for (index, v) in l.value.iter().enumerate() {
655 match v {
656 ValueOrHandler::Value(v) => {
657 list.insert_with_txn(txn, index, v.clone())?;
658 }
659 ValueOrHandler::Handler(h) => {
660 list.insert_container_with_txn(txn, index, h.clone())?;
661 }
662 }
663 }
664 l.attached = list.attached_handler().cloned();
665 Ok(list)
666 }
667 MaybeDetached::Attached(a) => {
668 let new_inner = create_handler(a, self_id);
669 let ans = new_inner.into_movable_list().unwrap();
670
671 for (i, v) in self.get_value().into_list().unwrap().iter().enumerate() {
672 if let LoroValue::Container(id) = v {
673 ans.insert_container_with_txn(txn, i, create_handler(a, id.clone()))?;
674 } else {
675 ans.insert_with_txn(txn, i, v.clone())?;
676 }
677 }
678
679 Ok(ans)
680 }
681 }
682 }
683
684 fn get_attached(&self) -> Option<Self> {
685 match &self.inner {
686 MaybeDetached::Detached(d) => d.lock().unwrap().attached.clone().map(|x| Self {
687 inner: MaybeDetached::Attached(x),
688 }),
689 MaybeDetached::Attached(_a) => Some(self.clone()),
690 }
691 }
692
693 fn doc(&self) -> Option<LoroDoc> {
694 match &self.inner {
695 MaybeDetached::Detached(_) => None,
696 MaybeDetached::Attached(a) => Some(a.doc()),
697 }
698 }
699}
700
701impl std::fmt::Debug for MovableListHandler {
702 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
703 write!(f, "MovableListHandler {}", self.id())
704 }
705}
706
707impl std::fmt::Debug for ListHandler {
708 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
709 match &self.inner {
710 MaybeDetached::Detached(_) => write!(f, "ListHandler Detached"),
711 MaybeDetached::Attached(a) => write!(f, "ListHandler {}", a.id),
712 }
713 }
714}
715
716impl HandlerTrait for ListHandler {
717 fn is_attached(&self) -> bool {
718 self.inner.is_attached()
719 }
720
721 fn attached_handler(&self) -> Option<&BasicHandler> {
722 self.inner.attached_handler()
723 }
724
725 fn get_value(&self) -> LoroValue {
726 match &self.inner {
727 MaybeDetached::Detached(a) => {
728 let a = a.lock().unwrap();
729 LoroValue::List(a.value.iter().map(|v| v.to_value()).collect())
730 }
731 MaybeDetached::Attached(a) => a.get_value(),
732 }
733 }
734
735 fn get_deep_value(&self) -> LoroValue {
736 match &self.inner {
737 MaybeDetached::Detached(a) => {
738 let a = a.lock().unwrap();
739 LoroValue::List(a.value.iter().map(|v| v.to_deep_value()).collect())
740 }
741 MaybeDetached::Attached(a) => a.get_deep_value(),
742 }
743 }
744
745 fn kind(&self) -> ContainerType {
746 ContainerType::List
747 }
748
749 fn to_handler(&self) -> Handler {
750 Handler::List(self.clone())
751 }
752
753 fn attach(
754 &self,
755 txn: &mut Transaction,
756 parent: &BasicHandler,
757 self_id: ContainerID,
758 ) -> LoroResult<Self> {
759 match &self.inner {
760 MaybeDetached::Detached(l) => {
761 let mut l = l.lock().unwrap();
762 let inner = create_handler(parent, self_id);
763 let list = inner.into_list().unwrap();
764 for (index, v) in l.value.iter().enumerate() {
765 match v {
766 ValueOrHandler::Value(v) => {
767 list.insert_with_txn(txn, index, v.clone())?;
768 }
769 ValueOrHandler::Handler(h) => {
770 list.insert_container_with_txn(txn, index, h.clone())?;
771 }
772 }
773 }
774 l.attached = list.attached_handler().cloned();
775 Ok(list)
776 }
777 MaybeDetached::Attached(a) => {
778 let new_inner = create_handler(a, self_id);
779 let ans = new_inner.into_list().unwrap();
780
781 for (i, v) in self.get_value().into_list().unwrap().iter().enumerate() {
782 if let LoroValue::Container(id) = v {
783 ans.insert_container_with_txn(txn, i, create_handler(a, id.clone()))?;
784 } else {
785 ans.insert_with_txn(txn, i, v.clone())?;
786 }
787 }
788
789 Ok(ans)
790 }
791 }
792 }
793
794 fn get_attached(&self) -> Option<Self> {
795 match &self.inner {
796 MaybeDetached::Detached(d) => d.lock().unwrap().attached.clone().map(|x| Self {
797 inner: MaybeDetached::Attached(x),
798 }),
799 MaybeDetached::Attached(_a) => Some(self.clone()),
800 }
801 }
802
803 fn from_handler(h: Handler) -> Option<Self> {
804 match h {
805 Handler::List(x) => Some(x),
806 _ => None,
807 }
808 }
809
810 fn doc(&self) -> Option<LoroDoc> {
811 match &self.inner {
812 MaybeDetached::Detached(_) => None,
813 MaybeDetached::Attached(a) => Some(a.doc()),
814 }
815 }
816}
817
818#[derive(Clone)]
819pub struct UnknownHandler {
820 inner: BasicHandler,
821}
822
823impl Debug for UnknownHandler {
824 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
825 write!(f, "UnknownHandler")
826 }
827}
828
829impl UnknownHandler {
830 pub fn is_deleted(&self) -> bool {
831 self.inner.is_deleted()
832 }
833}
834
835impl HandlerTrait for UnknownHandler {
836 fn is_attached(&self) -> bool {
837 true
838 }
839
840 fn attached_handler(&self) -> Option<&BasicHandler> {
841 Some(&self.inner)
842 }
843
844 fn get_value(&self) -> LoroValue {
845 todo!()
846 }
847
848 fn get_deep_value(&self) -> LoroValue {
849 todo!()
850 }
851
852 fn kind(&self) -> ContainerType {
853 self.inner.id.container_type()
854 }
855
856 fn to_handler(&self) -> Handler {
857 Handler::Unknown(self.clone())
858 }
859
860 fn from_handler(h: Handler) -> Option<Self> {
861 match h {
862 Handler::Unknown(x) => Some(x),
863 _ => None,
864 }
865 }
866
867 fn attach(
868 &self,
869 _txn: &mut Transaction,
870 _parent: &BasicHandler,
871 self_id: ContainerID,
872 ) -> LoroResult<Self> {
873 let new_inner = create_handler(&self.inner, self_id);
874 let ans = new_inner.into_unknown().unwrap();
875 Ok(ans)
876 }
877
878 fn get_attached(&self) -> Option<Self> {
879 Some(self.clone())
880 }
881
882 fn doc(&self) -> Option<LoroDoc> {
883 Some(self.inner.doc())
884 }
885}
886
887#[derive(Clone, EnumAsInner, Debug)]
888pub enum Handler {
889 Text(TextHandler),
890 Map(MapHandler),
891 List(ListHandler),
892 MovableList(MovableListHandler),
893 Tree(TreeHandler),
894 #[cfg(feature = "counter")]
895 Counter(counter::CounterHandler),
896 Unknown(UnknownHandler),
897}
898
899impl HandlerTrait for Handler {
900 fn is_attached(&self) -> bool {
901 match self {
902 Self::Text(x) => x.is_attached(),
903 Self::Map(x) => x.is_attached(),
904 Self::List(x) => x.is_attached(),
905 Self::Tree(x) => x.is_attached(),
906 Self::MovableList(x) => x.is_attached(),
907 #[cfg(feature = "counter")]
908 Self::Counter(x) => x.is_attached(),
909 Self::Unknown(x) => x.is_attached(),
910 }
911 }
912
913 fn attached_handler(&self) -> Option<&BasicHandler> {
914 match self {
915 Self::Text(x) => x.attached_handler(),
916 Self::Map(x) => x.attached_handler(),
917 Self::List(x) => x.attached_handler(),
918 Self::MovableList(x) => x.attached_handler(),
919 Self::Tree(x) => x.attached_handler(),
920 #[cfg(feature = "counter")]
921 Self::Counter(x) => x.attached_handler(),
922 Self::Unknown(x) => x.attached_handler(),
923 }
924 }
925
926 fn get_value(&self) -> LoroValue {
927 match self {
928 Self::Text(x) => x.get_value(),
929 Self::Map(x) => x.get_value(),
930 Self::List(x) => x.get_value(),
931 Self::MovableList(x) => x.get_value(),
932 Self::Tree(x) => x.get_value(),
933 #[cfg(feature = "counter")]
934 Self::Counter(x) => x.get_value(),
935 Self::Unknown(x) => x.get_value(),
936 }
937 }
938
939 fn get_deep_value(&self) -> LoroValue {
940 match self {
941 Self::Text(x) => x.get_deep_value(),
942 Self::Map(x) => x.get_deep_value(),
943 Self::List(x) => x.get_deep_value(),
944 Self::MovableList(x) => x.get_deep_value(),
945 Self::Tree(x) => x.get_deep_value(),
946 #[cfg(feature = "counter")]
947 Self::Counter(x) => x.get_deep_value(),
948 Self::Unknown(x) => x.get_deep_value(),
949 }
950 }
951
952 fn kind(&self) -> ContainerType {
953 match self {
954 Self::Text(x) => x.kind(),
955 Self::Map(x) => x.kind(),
956 Self::List(x) => x.kind(),
957 Self::MovableList(x) => x.kind(),
958 Self::Tree(x) => x.kind(),
959 #[cfg(feature = "counter")]
960 Self::Counter(x) => x.kind(),
961 Self::Unknown(x) => x.kind(),
962 }
963 }
964
965 fn to_handler(&self) -> Handler {
966 match self {
967 Self::Text(x) => x.to_handler(),
968 Self::Map(x) => x.to_handler(),
969 Self::List(x) => x.to_handler(),
970 Self::MovableList(x) => x.to_handler(),
971 Self::Tree(x) => x.to_handler(),
972 #[cfg(feature = "counter")]
973 Self::Counter(x) => x.to_handler(),
974 Self::Unknown(x) => x.to_handler(),
975 }
976 }
977
978 fn attach(
979 &self,
980 txn: &mut Transaction,
981 parent: &BasicHandler,
982 self_id: ContainerID,
983 ) -> LoroResult<Self> {
984 match self {
985 Self::Text(x) => Ok(Handler::Text(x.attach(txn, parent, self_id)?)),
986 Self::Map(x) => Ok(Handler::Map(x.attach(txn, parent, self_id)?)),
987 Self::List(x) => Ok(Handler::List(x.attach(txn, parent, self_id)?)),
988 Self::MovableList(x) => Ok(Handler::MovableList(x.attach(txn, parent, self_id)?)),
989 Self::Tree(x) => Ok(Handler::Tree(x.attach(txn, parent, self_id)?)),
990 #[cfg(feature = "counter")]
991 Self::Counter(x) => Ok(Handler::Counter(x.attach(txn, parent, self_id)?)),
992 Self::Unknown(x) => Ok(Handler::Unknown(x.attach(txn, parent, self_id)?)),
993 }
994 }
995
996 fn get_attached(&self) -> Option<Self> {
997 match self {
998 Self::Text(x) => x.get_attached().map(Handler::Text),
999 Self::Map(x) => x.get_attached().map(Handler::Map),
1000 Self::List(x) => x.get_attached().map(Handler::List),
1001 Self::MovableList(x) => x.get_attached().map(Handler::MovableList),
1002 Self::Tree(x) => x.get_attached().map(Handler::Tree),
1003 #[cfg(feature = "counter")]
1004 Self::Counter(x) => x.get_attached().map(Handler::Counter),
1005 Self::Unknown(x) => x.get_attached().map(Handler::Unknown),
1006 }
1007 }
1008
1009 fn from_handler(h: Handler) -> Option<Self> {
1010 Some(h)
1011 }
1012
1013 fn doc(&self) -> Option<LoroDoc> {
1014 match self {
1015 Self::Text(x) => x.doc(),
1016 Self::Map(x) => x.doc(),
1017 Self::List(x) => x.doc(),
1018 Self::MovableList(x) => x.doc(),
1019 Self::Tree(x) => x.doc(),
1020 #[cfg(feature = "counter")]
1021 Self::Counter(x) => x.doc(),
1022 Self::Unknown(x) => x.doc(),
1023 }
1024 }
1025}
1026
1027impl Handler {
1028 pub(crate) fn new_attached(id: ContainerID, doc: LoroDoc) -> Self {
1029 let kind = id.container_type();
1030 let handler = BasicHandler {
1031 container_idx: doc.arena.register_container(&id),
1032 id,
1033 doc,
1034 };
1035
1036 match kind {
1037 ContainerType::Map => Self::Map(MapHandler {
1038 inner: handler.into(),
1039 }),
1040 ContainerType::List => Self::List(ListHandler {
1041 inner: handler.into(),
1042 }),
1043 ContainerType::Tree => Self::Tree(TreeHandler {
1044 inner: handler.into(),
1045 }),
1046 ContainerType::Text => Self::Text(TextHandler {
1047 inner: handler.into(),
1048 }),
1049 ContainerType::MovableList => Self::MovableList(MovableListHandler {
1050 inner: handler.into(),
1051 }),
1052 #[cfg(feature = "counter")]
1053 ContainerType::Counter => Self::Counter(counter::CounterHandler {
1054 inner: handler.into(),
1055 }),
1056 ContainerType::Unknown(_) => Self::Unknown(UnknownHandler { inner: handler }),
1057 }
1058 }
1059
1060 #[allow(unused)]
1061 pub(crate) fn new_unattached(kind: ContainerType) -> Self {
1062 match kind {
1063 ContainerType::Text => Self::Text(TextHandler::new_detached()),
1064 ContainerType::Map => Self::Map(MapHandler::new_detached()),
1065 ContainerType::List => Self::List(ListHandler::new_detached()),
1066 ContainerType::Tree => Self::Tree(TreeHandler::new_detached()),
1067 ContainerType::MovableList => Self::MovableList(MovableListHandler::new_detached()),
1068 #[cfg(feature = "counter")]
1069 ContainerType::Counter => Self::Counter(counter::CounterHandler::new_detached()),
1070 ContainerType::Unknown(_) => unreachable!(),
1071 }
1072 }
1073
1074 pub fn id(&self) -> ContainerID {
1075 match self {
1076 Self::Map(x) => x.id(),
1077 Self::List(x) => x.id(),
1078 Self::Text(x) => x.id(),
1079 Self::Tree(x) => x.id(),
1080 Self::MovableList(x) => x.id(),
1081 #[cfg(feature = "counter")]
1082 Self::Counter(x) => x.id(),
1083 Self::Unknown(x) => x.id(),
1084 }
1085 }
1086
1087 pub(crate) fn container_idx(&self) -> ContainerIdx {
1088 match self {
1089 Self::Map(x) => x.idx(),
1090 Self::List(x) => x.idx(),
1091 Self::Text(x) => x.idx(),
1092 Self::Tree(x) => x.idx(),
1093 Self::MovableList(x) => x.idx(),
1094 #[cfg(feature = "counter")]
1095 Self::Counter(x) => x.idx(),
1096 Self::Unknown(x) => x.idx(),
1097 }
1098 }
1099
1100 pub fn c_type(&self) -> ContainerType {
1101 match self {
1102 Self::Map(_) => ContainerType::Map,
1103 Self::List(_) => ContainerType::List,
1104 Self::Text(_) => ContainerType::Text,
1105 Self::Tree(_) => ContainerType::Tree,
1106 Self::MovableList(_) => ContainerType::MovableList,
1107 #[cfg(feature = "counter")]
1108 Self::Counter(_) => ContainerType::Counter,
1109 Self::Unknown(x) => x.id().container_type(),
1110 }
1111 }
1112
1113 fn get_deep_value(&self) -> LoroValue {
1114 match self {
1115 Self::Map(x) => x.get_deep_value(),
1116 Self::List(x) => x.get_deep_value(),
1117 Self::MovableList(x) => x.get_deep_value(),
1118 Self::Text(x) => x.get_deep_value(),
1119 Self::Tree(x) => x.get_deep_value(),
1120 #[cfg(feature = "counter")]
1121 Self::Counter(x) => x.get_deep_value(),
1122 Self::Unknown(x) => x.get_deep_value(),
1123 }
1124 }
1125
1126 pub(crate) fn apply_diff(
1127 &self,
1128 diff: Diff,
1129 container_remap: &mut FxHashMap<ContainerID, ContainerID>,
1130 ) -> LoroResult<()> {
1131 let on_container_remap = &mut |old_id, new_id| {
1134 container_remap.insert(old_id, new_id);
1135 };
1136 match self {
1137 Self::Map(x) => {
1138 let diff = diff.into_map().unwrap();
1139 for (key, value) in diff.updated.into_iter() {
1140 match value.value {
1141 Some(ValueOrHandler::Handler(h)) => {
1142 let old_id = h.id();
1143 let new_h = x.insert_container(
1144 &key,
1145 Handler::new_unattached(old_id.container_type()),
1146 )?;
1147 let new_id = new_h.id();
1148 on_container_remap(old_id, new_id);
1149 }
1150 Some(ValueOrHandler::Value(LoroValue::Container(old_id))) => {
1151 let new_h = x.insert_container(
1152 &key,
1153 Handler::new_unattached(old_id.container_type()),
1154 )?;
1155 let new_id = new_h.id();
1156 on_container_remap(old_id, new_id);
1157 }
1158 Some(ValueOrHandler::Value(v)) => {
1159 x.insert_without_skipping(&key, v)?;
1160 }
1161 None => {
1162 x.delete(&key)?;
1163 }
1164 }
1165 }
1166 }
1167 Self::Text(x) => {
1168 let delta = diff.into_text().unwrap();
1169 x.apply_delta(&TextDelta::from_text_diff(delta.iter()))?;
1170 }
1171 Self::List(x) => {
1172 let delta = diff.into_list().unwrap();
1173 x.apply_delta(delta, on_container_remap)?;
1174 }
1175 Self::MovableList(x) => {
1176 let delta = diff.into_list().unwrap();
1177 x.apply_delta(delta, container_remap)?;
1178 }
1179 Self::Tree(x) => {
1180 fn remap_tree_id(
1181 id: &mut TreeID,
1182 container_remap: &FxHashMap<ContainerID, ContainerID>,
1183 ) {
1184 let mut remapped = false;
1185 let mut map_id = id.associated_meta_container();
1186 while let Some(rid) = container_remap.get(&map_id) {
1187 remapped = true;
1188 map_id = rid.clone();
1189 }
1190 if remapped {
1191 *id = TreeID::new(
1192 *map_id.as_normal().unwrap().0,
1193 *map_id.as_normal().unwrap().1,
1194 )
1195 }
1196 }
1197 for diff in diff.into_tree().unwrap().diff {
1198 let mut target = diff.target;
1199 match diff.action {
1200 TreeExternalDiff::Create {
1201 mut parent,
1202 index: _,
1203 position,
1204 } => {
1205 if let TreeParentId::Node(p) = &mut parent {
1206 remap_tree_id(p, container_remap)
1207 }
1208 remap_tree_id(&mut target, container_remap);
1209 if !x.is_node_unexist(&target) && !x.is_node_deleted(&target)? {
1210 x.move_at_with_target_for_apply_diff(parent, position, target)?;
1223 } else {
1224 let new_target = x.__internal__next_tree_id();
1225 if x.create_at_with_target_for_apply_diff(
1226 parent, position, new_target,
1227 )? {
1228 container_remap.insert(
1229 target.associated_meta_container(),
1230 new_target.associated_meta_container(),
1231 );
1232 }
1233 }
1234 }
1235 TreeExternalDiff::Move {
1236 mut parent,
1237 index: _,
1238 position,
1239 old_parent: _,
1240 old_index: _,
1241 } => {
1242 if let TreeParentId::Node(p) = &mut parent {
1243 remap_tree_id(p, container_remap)
1244 }
1245 remap_tree_id(&mut target, container_remap);
1246 if x.is_node_unexist(&target) || x.is_node_deleted(&target).unwrap() {
1248 let new_target = x.__internal__next_tree_id();
1250 if x.create_at_with_target_for_apply_diff(
1251 parent, position, new_target,
1252 )? {
1253 container_remap.insert(
1254 target.associated_meta_container(),
1255 new_target.associated_meta_container(),
1256 );
1257 }
1258 } else {
1259 x.move_at_with_target_for_apply_diff(parent, position, target)?;
1260 }
1261 }
1262 TreeExternalDiff::Delete { .. } => {
1263 remap_tree_id(&mut target, container_remap);
1264 if !x.is_node_deleted(&target).unwrap() {
1265 x.delete(target)?;
1266 }
1267 }
1268 }
1269 }
1270 }
1271 #[cfg(feature = "counter")]
1272 Self::Counter(x) => {
1273 let delta = diff.into_counter().unwrap();
1274 x.increment(delta)?;
1275 }
1276 Self::Unknown(_) => {
1277 }
1279 }
1280
1281 Ok(())
1282 }
1283
1284 pub fn clear(&self) -> LoroResult<()> {
1285 match self {
1286 Handler::Text(text_handler) => text_handler.clear(),
1287 Handler::Map(map_handler) => map_handler.clear(),
1288 Handler::List(list_handler) => list_handler.clear(),
1289 Handler::MovableList(movable_list_handler) => movable_list_handler.clear(),
1290 Handler::Tree(tree_handler) => tree_handler.clear(),
1291 #[cfg(feature = "counter")]
1292 Handler::Counter(counter_handler) => counter_handler.clear(),
1293 Handler::Unknown(_unknown_handler) => Ok(()),
1294 }
1295 }
1296}
1297
1298#[derive(Clone, EnumAsInner, Debug)]
1299pub enum ValueOrHandler {
1300 Value(LoroValue),
1301 Handler(Handler),
1302}
1303
1304impl ValueOrHandler {
1305 pub(crate) fn from_value(value: LoroValue, doc: &Arc<LoroDocInner>) -> Self {
1306 if let LoroValue::Container(c) = value {
1307 ValueOrHandler::Handler(Handler::new_attached(c, LoroDoc::from_inner(doc.clone())))
1308 } else {
1309 ValueOrHandler::Value(value)
1310 }
1311 }
1312
1313 pub(crate) fn to_value(&self) -> LoroValue {
1314 match self {
1315 Self::Value(v) => v.clone(),
1316 Self::Handler(h) => LoroValue::Container(h.id().clone()),
1317 }
1318 }
1319
1320 pub(crate) fn to_deep_value(&self) -> LoroValue {
1321 match self {
1322 Self::Value(v) => v.clone(),
1323 Self::Handler(h) => h.get_deep_value(),
1324 }
1325 }
1326}
1327
1328impl From<LoroValue> for ValueOrHandler {
1329 fn from(value: LoroValue) -> Self {
1330 ValueOrHandler::Value(value)
1331 }
1332}
1333
1334impl TextHandler {
1335 pub fn new_detached() -> Self {
1340 Self {
1341 inner: MaybeDetached::new_detached(RichtextState::default()),
1342 }
1343 }
1344
1345 pub fn version_id(&self) -> Option<usize> {
1349 match &self.inner {
1350 MaybeDetached::Detached(_) => None,
1351 MaybeDetached::Attached(a) => {
1352 Some(a.with_state(|state| state.as_richtext_state_mut().unwrap().get_version_id()))
1353 }
1354 }
1355 }
1356
1357 pub fn get_richtext_value(&self) -> LoroValue {
1358 match &self.inner {
1359 MaybeDetached::Detached(t) => {
1360 let t = t.lock().unwrap();
1361 t.value.get_richtext_value()
1362 }
1363 MaybeDetached::Attached(a) => {
1364 a.with_state(|state| state.as_richtext_state_mut().unwrap().get_richtext_value())
1365 }
1366 }
1367 }
1368
1369 pub fn is_empty(&self) -> bool {
1370 match &self.inner {
1371 MaybeDetached::Detached(t) => t.lock().unwrap().value.is_empty(),
1372 MaybeDetached::Attached(a) => {
1373 a.with_state(|state| state.as_richtext_state_mut().unwrap().is_empty())
1374 }
1375 }
1376 }
1377
1378 pub fn len_utf8(&self) -> usize {
1379 match &self.inner {
1380 MaybeDetached::Detached(t) => {
1381 let t = t.lock().unwrap();
1382 t.value.len_utf8()
1383 }
1384 MaybeDetached::Attached(a) => {
1385 a.with_state(|state| state.as_richtext_state_mut().unwrap().len_utf8())
1386 }
1387 }
1388 }
1389
1390 pub fn len_utf16(&self) -> usize {
1391 match &self.inner {
1392 MaybeDetached::Detached(t) => {
1393 let t = t.lock().unwrap();
1394 t.value.len_utf16()
1395 }
1396 MaybeDetached::Attached(a) => {
1397 a.with_state(|state| state.as_richtext_state_mut().unwrap().len_utf16())
1398 }
1399 }
1400 }
1401
1402 pub fn len_unicode(&self) -> usize {
1403 match &self.inner {
1404 MaybeDetached::Detached(t) => {
1405 let t = t.lock().unwrap();
1406 t.value.len_unicode()
1407 }
1408 MaybeDetached::Attached(a) => {
1409 a.with_state(|state| state.as_richtext_state_mut().unwrap().len_unicode())
1410 }
1411 }
1412 }
1413
1414 pub fn len_event(&self) -> usize {
1417 if cfg!(feature = "wasm") {
1418 self.len_utf16()
1419 } else {
1420 self.len_unicode()
1421 }
1422 }
1423
1424 fn len(&self, pos_type: PosType) -> usize {
1425 match &self.inner {
1426 MaybeDetached::Detached(t) => t.lock().unwrap().value.len(pos_type),
1427 MaybeDetached::Attached(a) => {
1428 a.with_state(|state| state.as_richtext_state_mut().unwrap().len(pos_type))
1429 }
1430 }
1431 }
1432
1433 pub fn diagnose(&self) {
1434 match &self.inner {
1435 MaybeDetached::Detached(t) => {
1436 let t = t.lock().unwrap();
1437 t.value.diagnose();
1438 }
1439 MaybeDetached::Attached(a) => {
1440 a.with_state(|state| state.as_richtext_state_mut().unwrap().diagnose());
1441 }
1442 }
1443 }
1444
1445 pub fn iter(&self, mut callback: impl FnMut(&str) -> bool) {
1446 match &self.inner {
1447 MaybeDetached::Detached(t) => {
1448 let t = t.lock().unwrap();
1449 for span in t.value.iter() {
1450 if !callback(span.text.as_str()) {
1451 return;
1452 }
1453 }
1454 }
1455 MaybeDetached::Attached(a) => {
1456 a.with_state(|state| {
1457 state.as_richtext_state_mut().unwrap().iter(callback);
1458 });
1459 }
1460 }
1461 }
1462
1463 pub fn char_at(&self, pos: usize, pos_type: PosType) -> LoroResult<char> {
1465 let len = self.len(pos_type);
1466 if pos >= len {
1467 return Err(LoroError::OutOfBound {
1468 pos,
1469 len,
1470 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1471 });
1472 }
1473 if let Ok(c) = match &self.inner {
1474 MaybeDetached::Detached(t) => {
1475 let t = t.lock().unwrap();
1476 let event_pos = match pos_type {
1477 PosType::Event => pos,
1478 _ => t.value.index_to_event_index(pos, pos_type),
1479 };
1480 t.value.get_char_by_event_index(event_pos)
1481 }
1482 MaybeDetached::Attached(a) => a.with_state(|state| {
1483 let state = state.as_richtext_state_mut().unwrap();
1484 let event_pos = match pos_type {
1485 PosType::Event => pos,
1486 _ => state.index_to_event_index(pos, pos_type),
1487 };
1488 state.get_char_by_event_index(event_pos)
1489 }),
1490 } {
1491 Ok(c)
1492 } else {
1493 Err(LoroError::OutOfBound {
1494 pos,
1495 len,
1496 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1497 })
1498 }
1499 }
1500
1501 pub fn slice(
1507 &self,
1508 start_index: usize,
1509 end_index: usize,
1510 pos_type: PosType,
1511 ) -> LoroResult<String> {
1512 self.slice_with_pos_type(start_index, end_index, pos_type)
1513 }
1514
1515 pub fn slice_utf16(&self, start_index: usize, end_index: usize) -> LoroResult<String> {
1516 self.slice(start_index, end_index, PosType::Utf16)
1517 }
1518
1519 fn slice_with_pos_type(
1520 &self,
1521 start_index: usize,
1522 end_index: usize,
1523 pos_type: PosType,
1524 ) -> LoroResult<String> {
1525 if end_index < start_index {
1526 return Err(LoroError::EndIndexLessThanStartIndex {
1527 start: start_index,
1528 end: end_index,
1529 });
1530 }
1531 if start_index == end_index {
1532 return Ok(String::new());
1533 }
1534
1535 let info = || format!("Position: {}:{}", file!(), line!()).into_boxed_str();
1536 match &self.inner {
1537 MaybeDetached::Detached(t) => {
1538 let t = t.lock().unwrap();
1539 let len = t.value.len(pos_type);
1540 if end_index > len {
1541 return Err(LoroError::OutOfBound {
1542 pos: end_index,
1543 len,
1544 info: info(),
1545 });
1546 }
1547 let (start, end) = match pos_type {
1548 PosType::Event => (start_index, end_index),
1549 _ => (
1550 t.value.index_to_event_index(start_index, pos_type),
1551 t.value.index_to_event_index(end_index, pos_type),
1552 ),
1553 };
1554 t.value.get_text_slice_by_event_index(start, end - start)
1555 }
1556 MaybeDetached::Attached(a) => a.with_state(|state| {
1557 let state = state.as_richtext_state_mut().unwrap();
1558 let len = state.len(pos_type);
1559 if end_index > len {
1560 return Err(LoroError::OutOfBound {
1561 pos: end_index,
1562 len,
1563 info: info(),
1564 });
1565 }
1566 let (start, end) = match pos_type {
1567 PosType::Event => (start_index, end_index),
1568 _ => (
1569 state.index_to_event_index(start_index, pos_type),
1570 state.index_to_event_index(end_index, pos_type),
1571 ),
1572 };
1573 state.get_text_slice_by_event_index(start, end - start)
1574 }),
1575 }
1576 }
1577
1578 pub fn slice_delta(
1579 &self,
1580 start_index: usize,
1581 end_index: usize,
1582 pos_type: PosType,
1583 ) -> LoroResult<Vec<TextDelta>> {
1584 match &self.inner {
1585 MaybeDetached::Detached(t) => {
1586 let t = t.lock().unwrap();
1587 let ans = t.value.slice_delta(start_index, end_index, pos_type)?;
1588 Ok(ans
1589 .into_iter()
1590 .map(|(s, a)| TextDelta::Insert {
1591 insert: s,
1592 attributes: a.to_option_map(),
1593 })
1594 .collect())
1595 }
1596 MaybeDetached::Attached(a) => a.with_state(|state| {
1597 let ans = state.as_richtext_state_mut().unwrap().slice_delta(
1598 start_index,
1599 end_index,
1600 pos_type,
1601 )?;
1602 Ok(ans
1603 .into_iter()
1604 .map(|(s, a)| TextDelta::Insert {
1605 insert: s,
1606 attributes: a.to_option_map(),
1607 })
1608 .collect())
1609 }),
1610 }
1611 }
1612
1613 pub fn splice(&self, pos: usize, len: usize, s: &str, pos_type: PosType) -> LoroResult<String> {
1620 let x = self.slice(pos, pos + len, pos_type)?;
1621 self.delete(pos, len, pos_type)?;
1622 self.insert(pos, s, pos_type)?;
1623 Ok(x)
1624 }
1625
1626 pub fn splice_utf8(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1627 self.delete_utf8(pos, len)?;
1629 self.insert_utf8(pos, s)?;
1630 Ok(())
1631 }
1632
1633 pub fn splice_utf16(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1634 self.delete(pos, len, PosType::Utf16)?;
1635 self.insert(pos, s, PosType::Utf16)?;
1636 Ok(())
1637 }
1638
1639 pub fn insert(&self, pos: usize, s: &str, pos_type: PosType) -> LoroResult<()> {
1643 match &self.inner {
1644 MaybeDetached::Detached(t) => {
1645 let mut t = t.lock().unwrap();
1646 let (index, _) = t
1647 .value
1648 .get_entity_index_for_text_insert(pos, pos_type)
1649 .unwrap();
1650 t.value.insert_at_entity_index(
1651 index,
1652 BytesSlice::from_bytes(s.as_bytes()),
1653 IdFull::NONE_ID,
1654 );
1655 Ok(())
1656 }
1657 MaybeDetached::Attached(a) => {
1658 a.with_txn(|txn| self.insert_with_txn(txn, pos, s, pos_type))
1659 }
1660 }
1661 }
1662
1663 pub fn insert_utf8(&self, pos: usize, s: &str) -> LoroResult<()> {
1664 self.insert(pos, s, PosType::Bytes)
1665 }
1666
1667 pub fn insert_utf16(&self, pos: usize, s: &str) -> LoroResult<()> {
1668 self.insert(pos, s, PosType::Utf16)
1669 }
1670
1671 pub fn insert_unicode(&self, pos: usize, s: &str) -> LoroResult<()> {
1672 self.insert(pos, s, PosType::Unicode)
1673 }
1674
1675 pub fn insert_with_txn(
1677 &self,
1678 txn: &mut Transaction,
1679 pos: usize,
1680 s: &str,
1681 pos_type: PosType,
1682 ) -> LoroResult<()> {
1683 self.insert_with_txn_and_attr(txn, pos, s, None, pos_type)?;
1684 Ok(())
1685 }
1686
1687 pub fn insert_with_txn_utf8(
1688 &self,
1689 txn: &mut Transaction,
1690 pos: usize,
1691 s: &str,
1692 ) -> LoroResult<()> {
1693 self.insert_with_txn(txn, pos, s, PosType::Bytes)
1694 }
1695
1696 pub fn delete(&self, pos: usize, len: usize, pos_type: PosType) -> LoroResult<()> {
1700 match &self.inner {
1701 MaybeDetached::Detached(t) => {
1702 let mut t = t.lock().unwrap();
1703 let ranges = t.value.get_text_entity_ranges(pos, len, pos_type)?;
1704 for range in ranges.iter().rev() {
1705 t.value
1706 .drain_by_entity_index(range.entity_start, range.entity_len(), None);
1707 }
1708 Ok(())
1709 }
1710 MaybeDetached::Attached(a) => {
1711 a.with_txn(|txn| self.delete_with_txn(txn, pos, len, pos_type))
1712 }
1713 }
1714 }
1715
1716 pub fn delete_utf8(&self, pos: usize, len: usize) -> LoroResult<()> {
1717 self.delete(pos, len, PosType::Bytes)
1718 }
1719
1720 pub fn delete_utf16(&self, pos: usize, len: usize) -> LoroResult<()> {
1721 self.delete(pos, len, PosType::Utf16)
1722 }
1723
1724 pub fn delete_unicode(&self, pos: usize, len: usize) -> LoroResult<()> {
1725 self.delete(pos, len, PosType::Unicode)
1726 }
1727
1728 fn insert_with_txn_and_attr(
1731 &self,
1732 txn: &mut Transaction,
1733 pos: usize,
1734 s: &str,
1735 attr: Option<&FxHashMap<String, LoroValue>>,
1736 pos_type: PosType,
1737 ) -> Result<Vec<(InternalString, LoroValue)>, LoroError> {
1738 if s.is_empty() {
1739 return Ok(Vec::new());
1740 }
1741
1742 match pos_type {
1743 PosType::Event => {
1744 if pos > self.len_event() {
1745 return Err(LoroError::OutOfBound {
1746 pos,
1747 len: self.len_event(),
1748 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1749 });
1750 }
1751 }
1752 PosType::Bytes => {
1753 if pos > self.len_utf8() {
1754 return Err(LoroError::OutOfBound {
1755 pos,
1756 len: self.len_utf8(),
1757 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1758 });
1759 }
1760 }
1761 PosType::Unicode => {
1762 if pos > self.len_unicode() {
1763 return Err(LoroError::OutOfBound {
1764 pos,
1765 len: self.len_unicode(),
1766 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1767 });
1768 }
1769 }
1770 PosType::Entity => {}
1771 PosType::Utf16 => {
1772 if pos > self.len_utf16() {
1773 return Err(LoroError::OutOfBound {
1774 pos,
1775 len: self.len_utf16(),
1776 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1777 });
1778 }
1779 }
1780 }
1781
1782 let inner = self.inner.try_attached_state()?;
1783 let (entity_index, event_index, styles) = inner.with_state(|state| {
1784 let richtext_state = state.as_richtext_state_mut().unwrap();
1785 let ret = richtext_state.get_entity_index_for_text_insert(pos, pos_type);
1786 let (entity_index, cursor) = match ret {
1787 Err(_) => match pos_type {
1788 PosType::Bytes => {
1789 return (
1790 Err(LoroError::UTF8InUnicodeCodePoint { pos }),
1791 0,
1792 StyleMeta::empty(),
1793 );
1794 }
1795 PosType::Utf16 | PosType::Event => {
1796 return (
1797 Err(LoroError::UTF16InUnicodeCodePoint { pos }),
1798 0,
1799 StyleMeta::empty(),
1800 );
1801 }
1802 _ => unreachable!(),
1803 },
1804 Ok(x) => x,
1805 };
1806 let event_index = if let Some(cursor) = cursor {
1807 if pos_type == PosType::Event {
1808 debug_assert_eq!(
1809 richtext_state.get_event_index_by_cursor(cursor),
1810 pos,
1811 "pos={} cursor={:?} state={:#?}",
1812 pos,
1813 cursor,
1814 &richtext_state
1815 );
1816 pos
1817 } else {
1818 richtext_state.get_event_index_by_cursor(cursor)
1819 }
1820 } else {
1821 assert_eq!(entity_index, 0);
1822 0
1823 };
1824 let styles = richtext_state.get_styles_at_entity_index(entity_index);
1825 (Ok(entity_index), event_index, styles)
1826 });
1827
1828 let entity_index = match entity_index {
1829 Err(x) => return Err(x),
1830 _ => entity_index.unwrap(),
1831 };
1832
1833 let mut override_styles = Vec::new();
1834 if let Some(attr) = attr {
1835 let map: FxHashMap<_, _> = styles.iter().map(|x| (x.0.clone(), x.1.data)).collect();
1837 for (key, style) in map.iter() {
1838 match attr.get(key.deref()) {
1839 Some(v) if v == style => {}
1840 new_style_value => {
1841 let new_style_value = new_style_value.cloned().unwrap_or(LoroValue::Null);
1843 override_styles.push((key.clone(), new_style_value));
1844 }
1845 }
1846 }
1847
1848 for (key, style) in attr.iter() {
1849 let key = key.as_str().into();
1850 if !map.contains_key(&key) {
1851 override_styles.push((key, style.clone()));
1852 }
1853 }
1854 }
1855
1856 let unicode_len = s.chars().count();
1857 let event_len = if cfg!(feature = "wasm") {
1858 count_utf16_len(s.as_bytes())
1859 } else {
1860 unicode_len
1861 };
1862
1863 txn.apply_local_op(
1864 inner.container_idx,
1865 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
1866 slice: ListSlice::RawStr {
1867 str: Cow::Borrowed(s),
1868 unicode_len,
1869 },
1870 pos: entity_index,
1871 }),
1872 EventHint::InsertText {
1873 pos: event_index as u32,
1874 styles,
1875 unicode_len: unicode_len as u32,
1876 event_len: event_len as u32,
1877 },
1878 &inner.doc,
1879 )?;
1880
1881 Ok(override_styles)
1882 }
1883
1884 pub fn delete_with_txn(
1886 &self,
1887 txn: &mut Transaction,
1888 pos: usize,
1889 len: usize,
1890 pos_type: PosType,
1891 ) -> LoroResult<()> {
1892 self.delete_with_txn_inline(txn, pos, len, pos_type)
1893 }
1894
1895 fn delete_with_txn_inline(
1896 &self,
1897 txn: &mut Transaction,
1898 pos: usize,
1899 len: usize,
1900 pos_type: PosType,
1901 ) -> LoroResult<()> {
1902 if len == 0 {
1903 return Ok(());
1904 }
1905
1906 if pos + len > self.len(pos_type) {
1907 error!("pos={} len={} len_event={}", pos, len, self.len_event());
1908 return Err(LoroError::OutOfBound {
1909 pos: pos + len,
1910 len: self.len_event(),
1911 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1912 });
1913 }
1914
1915 let inner = self.inner.try_attached_state()?;
1916 let s = tracing::span!(tracing::Level::INFO, "delete", "pos={} len={}", pos, len);
1917 let _e = s.enter();
1918 let mut event_pos = 0;
1919 let mut event_len = 0;
1920 let ranges = inner.with_state(|state| {
1921 let richtext_state = state.as_richtext_state_mut().unwrap();
1922 event_pos = richtext_state.index_to_event_index(pos, pos_type);
1923 let event_end = richtext_state.index_to_event_index(pos + len, pos_type);
1924 event_len = event_end - event_pos;
1925
1926 richtext_state.get_text_entity_ranges_in_event_index_range(event_pos, event_len)
1927 })?;
1928
1929 let pos = event_pos as isize;
1931 let len = event_len as isize;
1932 let mut event_end = pos + len;
1933 for range in ranges.iter().rev() {
1934 let event_start = event_end - range.event_len as isize;
1935 txn.apply_local_op(
1936 inner.container_idx,
1937 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
1938 range.id_start,
1939 range.entity_start as isize,
1940 range.entity_len() as isize,
1941 ))),
1942 EventHint::DeleteText {
1943 span: DeleteSpan {
1944 pos: event_start,
1945 signed_len: range.event_len as isize,
1946 },
1947 unicode_len: range.entity_len(),
1948 },
1949 &inner.doc,
1950 )?;
1951 event_end = event_start;
1952 }
1953
1954 Ok(())
1955 }
1956
1957 pub fn mark(
1961 &self,
1962 start: usize,
1963 end: usize,
1964 key: impl Into<InternalString>,
1965 value: LoroValue,
1966 pos_type: PosType,
1967 ) -> LoroResult<()> {
1968 match &self.inner {
1969 MaybeDetached::Detached(t) => {
1970 let mut g = t.lock().unwrap();
1971 self.mark_for_detached(&mut g.value, key, &value, start, end, pos_type, false)
1972 }
1973 MaybeDetached::Attached(a) => {
1974 a.with_txn(|txn| self.mark_with_txn(txn, start, end, key, value, pos_type, false))
1975 }
1976 }
1977 }
1978
1979 fn mark_for_detached(
1980 &self,
1981 state: &mut RichtextState,
1982 key: impl Into<InternalString>,
1983 value: &LoroValue,
1984 start: usize,
1985 end: usize,
1986 pos_type: PosType,
1987 is_delete: bool,
1988 ) -> Result<(), LoroError> {
1989 let key: InternalString = key.into();
1990 if start >= end {
1991 return Err(loro_common::LoroError::ArgErr(
1992 "Start must be less than end".to_string().into_boxed_str(),
1993 ));
1994 }
1995
1996 let len = state.len(pos_type);
1997 if end > len {
1998 return Err(LoroError::OutOfBound {
1999 pos: end,
2000 len,
2001 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2002 });
2003 }
2004 let (entity_range, styles) =
2005 state.get_entity_range_and_text_styles_at_range(start..end, pos_type);
2006 if let Some(styles) = styles {
2007 if styles.has_key_value(&key, value) {
2008 return Ok(());
2010 }
2011 }
2012
2013 let style_op = Arc::new(StyleOp {
2014 lamport: 0,
2015 peer: 0,
2016 cnt: 0,
2017 key,
2018 value: value.clone(),
2019 info: if is_delete {
2021 TextStyleInfoFlag::BOLD.to_delete()
2022 } else {
2023 TextStyleInfoFlag::BOLD
2024 },
2025 });
2026 state.mark_with_entity_index(entity_range, style_op);
2027 Ok(())
2028 }
2029
2030 pub fn unmark(
2032 &self,
2033 start: usize,
2034 end: usize,
2035 key: impl Into<InternalString>,
2036 pos_type: PosType,
2037 ) -> LoroResult<()> {
2038 match &self.inner {
2039 MaybeDetached::Detached(t) => self.mark_for_detached(
2040 &mut t.lock().unwrap().value,
2041 key,
2042 &LoroValue::Null,
2043 start,
2044 end,
2045 pos_type,
2046 true,
2047 ),
2048 MaybeDetached::Attached(a) => a.with_txn(|txn| {
2049 self.mark_with_txn(txn, start, end, key, LoroValue::Null, pos_type, true)
2050 }),
2051 }
2052 }
2053
2054 pub fn mark_with_txn(
2056 &self,
2057 txn: &mut Transaction,
2058 start: usize,
2059 end: usize,
2060 key: impl Into<InternalString>,
2061 value: LoroValue,
2062 pos_type: PosType,
2063 is_delete: bool,
2064 ) -> LoroResult<()> {
2065 if start >= end {
2066 return Err(loro_common::LoroError::ArgErr(
2067 "Start must be less than end".to_string().into_boxed_str(),
2068 ));
2069 }
2070
2071 let inner = self.inner.try_attached_state()?;
2072 let key: InternalString = key.into();
2073
2074 let mut doc_state = inner.doc.state.lock().unwrap();
2075 let len = doc_state.with_state_mut(inner.container_idx, |state| {
2076 state.as_richtext_state_mut().unwrap().len(pos_type)
2077 });
2078
2079 if end > len {
2080 return Err(LoroError::OutOfBound {
2081 pos: end,
2082 len,
2083 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2084 });
2085 }
2086
2087 let (entity_range, skip, event_start, event_end) =
2088 doc_state.with_state_mut(inner.container_idx, |state| {
2089 let state = state.as_richtext_state_mut().unwrap();
2090 let event_start = state.index_to_event_index(start, pos_type);
2091 let event_end = state.index_to_event_index(end, pos_type);
2092 let (entity_range, styles) =
2093 state.get_entity_range_and_styles_at_range(start..end, pos_type);
2094
2095 let skip = match styles {
2096 Some(styles) if styles.has_key_value(&key, &value) => {
2097 true
2099 }
2100 _ => false,
2101 };
2102
2103 (entity_range, skip, event_start, event_end)
2104 });
2105
2106 if skip {
2107 return Ok(());
2108 }
2109
2110 let entity_start = entity_range.start;
2111 let entity_end = entity_range.end;
2112 let style_config = doc_state.config.text_style_config.try_read().unwrap();
2113 let flag = if is_delete {
2114 style_config
2115 .get_style_flag_for_unmark(&key)
2116 .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2117 } else {
2118 style_config
2119 .get_style_flag(&key)
2120 .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2121 };
2122
2123 drop(style_config);
2124 drop(doc_state);
2125 txn.apply_local_op(
2126 inner.container_idx,
2127 crate::op::RawOpContent::List(ListOp::StyleStart {
2128 start: entity_start as u32,
2129 end: entity_end as u32,
2130 key: key.clone(),
2131 value: value.clone(),
2132 info: flag,
2133 }),
2134 EventHint::Mark {
2135 start: event_start as u32,
2136 end: event_end as u32,
2137 style: crate::container::richtext::Style { key, data: value },
2138 },
2139 &inner.doc,
2140 )?;
2141
2142 txn.apply_local_op(
2143 inner.container_idx,
2144 crate::op::RawOpContent::List(ListOp::StyleEnd),
2145 EventHint::MarkEnd,
2146 &inner.doc,
2147 )?;
2148
2149 Ok(())
2150 }
2151
2152 pub fn check(&self) {
2153 match &self.inner {
2154 MaybeDetached::Detached(t) => {
2155 let t = t.lock().unwrap();
2156 t.value.check_consistency_between_content_and_style_ranges();
2157 }
2158 MaybeDetached::Attached(a) => a.with_state(|state| {
2159 state
2160 .as_richtext_state_mut()
2161 .unwrap()
2162 .check_consistency_between_content_and_style_ranges();
2163 }),
2164 }
2165 }
2166
2167 pub fn apply_delta(&self, delta: &[TextDelta]) -> LoroResult<()> {
2168 match &self.inner {
2169 MaybeDetached::Detached(t) => {
2170 let _t = t.lock().unwrap();
2171 Err(LoroError::NotImplemented(
2173 "`apply_delta` on a detached text container",
2174 ))
2175 }
2176 MaybeDetached::Attached(a) => a.with_txn(|txn| self.apply_delta_with_txn(txn, delta)),
2177 }
2178 }
2179
2180 pub fn apply_delta_with_txn(
2181 &self,
2182 txn: &mut Transaction,
2183 delta: &[TextDelta],
2184 ) -> LoroResult<()> {
2185 let mut index = 0;
2186 struct PendingMark {
2187 start: usize,
2188 end: usize,
2189 attributes: FxHashMap<InternalString, LoroValue>,
2190 }
2191 let mut marks: Vec<PendingMark> = Vec::new();
2192 for d in delta {
2193 match d {
2194 TextDelta::Insert { insert, attributes } => {
2195 let insert_len = event_len(insert.as_str());
2196 if insert_len == 0 {
2197 continue;
2198 }
2199
2200 let mut empty_attr = None;
2201 let attr_ref = attributes.as_ref().unwrap_or_else(|| {
2202 empty_attr = Some(FxHashMap::default());
2203 empty_attr.as_ref().unwrap()
2204 });
2205
2206 let end = index + insert_len;
2207 let override_styles = self.insert_with_txn_and_attr(
2208 txn,
2209 index,
2210 insert.as_str(),
2211 Some(attr_ref),
2212 PosType::Event,
2213 )?;
2214
2215 let mut pending_mark = PendingMark {
2216 start: index,
2217 end,
2218 attributes: FxHashMap::default(),
2219 };
2220 for (key, value) in override_styles {
2221 pending_mark.attributes.insert(key, value);
2222 }
2223 marks.push(pending_mark);
2224 index = end;
2225 }
2226 TextDelta::Delete { delete } => {
2227 self.delete_with_txn(txn, index, *delete, PosType::Event)?;
2228 }
2229 TextDelta::Retain { attributes, retain } => {
2230 let end = index + *retain;
2231 match attributes {
2232 Some(attr) if !attr.is_empty() => {
2233 let mut pending_mark = PendingMark {
2234 start: index,
2235 end,
2236 attributes: FxHashMap::default(),
2237 };
2238 for (key, value) in attr {
2239 pending_mark
2240 .attributes
2241 .insert(key.deref().into(), value.clone());
2242 }
2243 marks.push(pending_mark);
2244 }
2245 _ => {}
2246 }
2247 index = end;
2248 }
2249 }
2250 }
2251
2252 let mut len = self.len_event();
2253 for pending_mark in marks {
2254 if pending_mark.start >= len {
2255 self.insert_with_txn(
2256 txn,
2257 len,
2258 &"\n".repeat(pending_mark.start - len + 1),
2259 PosType::Event,
2260 )?;
2261 len = pending_mark.start;
2262 }
2263
2264 for (key, value) in pending_mark.attributes {
2265 self.mark_with_txn(
2266 txn,
2267 pending_mark.start,
2268 pending_mark.end,
2269 key.deref(),
2270 value,
2271 PosType::Event,
2272 false,
2273 )?;
2274 }
2275 }
2276
2277 Ok(())
2278 }
2279
2280 pub fn update(&self, text: &str, options: UpdateOptions) -> Result<(), UpdateTimeoutError> {
2281 let old_str = self.to_string();
2282 let new = text.chars().map(|x| x as u32).collect::<Vec<u32>>();
2283 let old = old_str.chars().map(|x| x as u32).collect::<Vec<u32>>();
2284 diff(
2285 &mut OperateProxy::new(text_update::DiffHook::new(self, &new)),
2286 options,
2287 &old,
2288 &new,
2289 )?;
2290 Ok(())
2291 }
2292
2293 pub fn update_by_line(
2294 &self,
2295 text: &str,
2296 options: UpdateOptions,
2297 ) -> Result<(), UpdateTimeoutError> {
2298 let hook = text_update::DiffHookForLine::new(self, text);
2299 let old_lines = hook.get_old_arr().to_vec();
2300 let new_lines = hook.get_new_arr().to_vec();
2301 diff(
2302 &mut OperateProxy::new(hook),
2303 options,
2304 &old_lines,
2305 &new_lines,
2306 )
2307 }
2308
2309 #[allow(clippy::inherent_to_string)]
2310 pub fn to_string(&self) -> String {
2311 match &self.inner {
2312 MaybeDetached::Detached(t) => t.lock().unwrap().value.to_string(),
2313 MaybeDetached::Attached(a) => a.get_value().into_string().unwrap().unwrap(),
2314 }
2315 }
2316
2317 pub fn get_cursor(&self, event_index: usize, side: Side) -> Option<Cursor> {
2318 self.get_cursor_internal(event_index, side, true)
2319 }
2320
2321 pub(crate) fn get_cursor_internal(
2323 &self,
2324 index: usize,
2325 side: Side,
2326 get_by_event_index: bool,
2327 ) -> Option<Cursor> {
2328 match &self.inner {
2329 MaybeDetached::Detached(_) => None,
2330 MaybeDetached::Attached(a) => {
2331 let (id, len, origin_pos) = a.with_state(|s| {
2332 let s = s.as_richtext_state_mut().unwrap();
2333 (
2334 s.get_stable_position(index, get_by_event_index),
2335 if get_by_event_index {
2336 s.len_event()
2337 } else {
2338 s.len_unicode()
2339 },
2340 if get_by_event_index {
2341 s.event_index_to_unicode_index(index)
2342 } else {
2343 index
2344 },
2345 )
2346 });
2347
2348 if len == 0 {
2349 return Some(Cursor {
2350 id: None,
2351 container: self.id(),
2352 side: if side == Side::Middle {
2353 Side::Left
2354 } else {
2355 side
2356 },
2357 origin_pos: 0,
2358 });
2359 }
2360
2361 if len <= index {
2362 return Some(Cursor {
2363 id: None,
2364 container: self.id(),
2365 side: Side::Right,
2366 origin_pos: len,
2367 });
2368 }
2369
2370 let id = id?;
2371 Some(Cursor {
2372 id: Some(id),
2373 container: self.id(),
2374 side,
2375 origin_pos,
2376 })
2377 }
2378 }
2379 }
2380
2381 pub(crate) fn convert_entity_index_to_event_index(&self, entity_index: usize) -> usize {
2382 match &self.inner {
2383 MaybeDetached::Detached(s) => s
2384 .lock()
2385 .unwrap()
2386 .value
2387 .entity_index_to_event_index(entity_index),
2388 MaybeDetached::Attached(a) => {
2389 let mut pos = 0;
2390 a.with_state(|s| {
2391 let s = s.as_richtext_state_mut().unwrap();
2392 pos = s.entity_index_to_event_index(entity_index);
2393 });
2394 pos
2395 }
2396 }
2397 }
2398
2399 pub fn get_delta(&self) -> Vec<TextDelta> {
2400 match &self.inner {
2401 MaybeDetached::Detached(s) => {
2402 let mut delta = Vec::new();
2403 for span in s.lock().unwrap().value.iter() {
2404 if span.text.as_str().is_empty() {
2405 continue;
2406 }
2407
2408 let next_attr = span.attributes.to_option_map();
2409 match delta.last_mut() {
2410 Some(TextDelta::Insert { insert, attributes })
2411 if &next_attr == attributes =>
2412 {
2413 insert.push_str(span.text.as_str());
2414 continue;
2415 }
2416 _ => {}
2417 }
2418
2419 delta.push(TextDelta::Insert {
2420 insert: span.text.as_str().to_string(),
2421 attributes: next_attr,
2422 })
2423 }
2424 delta
2425 }
2426 MaybeDetached::Attached(_a) => self
2427 .with_state(|state| {
2428 let state = state.as_richtext_state_mut().unwrap();
2429 Ok(state.get_delta())
2430 })
2431 .unwrap(),
2432 }
2433 }
2434
2435 pub fn is_deleted(&self) -> bool {
2436 match &self.inner {
2437 MaybeDetached::Detached(_) => false,
2438 MaybeDetached::Attached(a) => a.is_deleted(),
2439 }
2440 }
2441
2442 pub fn push_str(&self, s: &str) -> LoroResult<()> {
2443 self.insert_utf8(self.len_utf8(), s)
2444 }
2445
2446 pub fn clear(&self) -> LoroResult<()> {
2447 match &self.inner {
2448 MaybeDetached::Detached(mutex) => {
2449 let mut t = mutex.lock().unwrap();
2450 let len = t.value.len_unicode();
2451 let ranges = t.value.get_text_entity_ranges(0, len, PosType::Unicode)?;
2452 for range in ranges.iter().rev() {
2453 t.value
2454 .drain_by_entity_index(range.entity_start, range.entity_len(), None);
2455 }
2456 Ok(())
2457 }
2458 MaybeDetached::Attached(a) => a.with_txn(|txn| {
2459 let len = a.with_state(|s| s.as_richtext_state_mut().unwrap().len_unicode());
2460 self.delete_with_txn_inline(txn, 0, len, PosType::Unicode)
2461 }),
2462 }
2463 }
2464
2465 pub fn convert_pos(&self, index: usize, from: PosType, to: PosType) -> Option<usize> {
2470 if from == to {
2471 return Some(index);
2472 }
2473
2474 if matches!(from, PosType::Entity) || matches!(to, PosType::Entity) {
2475 return None;
2476 }
2477
2478 let (event_index, unicode_index) = match &self.inner {
2480 MaybeDetached::Detached(t) => {
2481 let t = t.lock().unwrap();
2482 if index > t.value.len(from) {
2483 return None;
2484 }
2485 let event_index = if from == PosType::Event {
2486 index
2487 } else {
2488 t.value.index_to_event_index(index, from)
2489 };
2490 let unicode_index = if from == PosType::Unicode {
2491 index
2492 } else {
2493 t.value.event_index_to_unicode_index(event_index)
2494 };
2495 (event_index, unicode_index)
2496 }
2497 MaybeDetached::Attached(a) => {
2498 let res: Option<(usize, usize)> = a.with_state(|state| {
2499 let state = state.as_richtext_state_mut().unwrap();
2500 if index > state.len(from) {
2501 return None;
2502 }
2503
2504 let event_index = if from == PosType::Event {
2505 index
2506 } else {
2507 state.index_to_event_index(index, from)
2508 };
2509 let unicode_index = if from == PosType::Unicode {
2510 index
2511 } else {
2512 state.event_index_to_unicode_index(event_index)
2513 };
2514 Some((event_index, unicode_index))
2515 });
2516
2517 match res {
2518 Some(v) => v,
2519 None => return None,
2520 }
2521 }
2522 };
2523
2524 let result = match to {
2525 PosType::Unicode => Some(unicode_index),
2526 PosType::Event => Some(event_index),
2527 PosType::Bytes | PosType::Utf16 => {
2528 let prefix = match &self.inner {
2530 MaybeDetached::Detached(t) => {
2531 let t = t.lock().unwrap();
2532 if event_index > t.value.len_event() {
2533 return None;
2534 }
2535 t.value.get_text_slice_by_event_index(0, event_index).ok()?
2536 }
2537 MaybeDetached::Attached(a) => {
2538 let res: Result<String, ()> = a.with_state(|state| {
2539 let state = state.as_richtext_state_mut().unwrap();
2540 if event_index > state.len_event() {
2541 return Err(());
2542 }
2543 state
2544 .get_text_slice_by_event_index(0, event_index)
2545 .map_err(|_| ())
2546 });
2547
2548 match res {
2549 Ok(v) => v,
2550 Err(_) => return None,
2551 }
2552 }
2553 };
2554
2555 Some(match to {
2556 PosType::Bytes => prefix.len(),
2557 PosType::Utf16 => count_utf16_len(prefix.as_bytes()),
2558 _ => unreachable!(),
2559 })
2560 }
2561 PosType::Entity => None,
2562 };
2563 result
2564 }
2565}
2566
2567fn event_len(s: &str) -> usize {
2568 if cfg!(feature = "wasm") {
2569 count_utf16_len(s.as_bytes())
2570 } else {
2571 s.chars().count()
2572 }
2573}
2574
2575impl ListHandler {
2576 pub fn new_detached() -> Self {
2580 Self {
2581 inner: MaybeDetached::new_detached(Vec::new()),
2582 }
2583 }
2584
2585 pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
2586 match &self.inner {
2587 MaybeDetached::Detached(l) => {
2588 let mut list = l.lock().unwrap();
2589 list.value.insert(pos, ValueOrHandler::Value(v.into()));
2590 Ok(())
2591 }
2592 MaybeDetached::Attached(a) => {
2593 a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
2594 }
2595 }
2596 }
2597
2598 pub fn insert_with_txn(
2599 &self,
2600 txn: &mut Transaction,
2601 pos: usize,
2602 v: LoroValue,
2603 ) -> LoroResult<()> {
2604 if pos > self.len() {
2605 return Err(LoroError::OutOfBound {
2606 pos,
2607 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2608 len: self.len(),
2609 });
2610 }
2611
2612 let inner = self.inner.try_attached_state()?;
2613 if let Some(_container) = v.as_container() {
2614 return Err(LoroError::ArgErr(
2615 INSERT_CONTAINER_VALUE_ARG_ERROR
2616 .to_string()
2617 .into_boxed_str(),
2618 ));
2619 }
2620
2621 txn.apply_local_op(
2622 inner.container_idx,
2623 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
2624 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
2625 pos,
2626 }),
2627 EventHint::InsertList { len: 1, pos },
2628 &inner.doc,
2629 )
2630 }
2631
2632 pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
2633 match &self.inner {
2634 MaybeDetached::Detached(l) => {
2635 let mut list = l.lock().unwrap();
2636 list.value.push(ValueOrHandler::Value(v.into()));
2637 Ok(())
2638 }
2639 MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v.into())),
2640 }
2641 }
2642
2643 pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
2644 let pos = self.len();
2645 self.insert_with_txn(txn, pos, v)
2646 }
2647
2648 pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
2649 match &self.inner {
2650 MaybeDetached::Detached(l) => {
2651 let mut list = l.lock().unwrap();
2652 Ok(list.value.pop().map(|v| v.to_value()))
2653 }
2654 MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
2655 }
2656 }
2657
2658 pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
2659 let len = self.len();
2660 if len == 0 {
2661 return Ok(None);
2662 }
2663
2664 let v = self.get(len - 1);
2665 self.delete_with_txn(txn, len - 1, 1)?;
2666 Ok(v)
2667 }
2668
2669 pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
2670 match &self.inner {
2671 MaybeDetached::Detached(l) => {
2672 let mut list = l.lock().unwrap();
2673 list.value
2674 .insert(pos, ValueOrHandler::Handler(child.to_handler()));
2675 Ok(child)
2676 }
2677 MaybeDetached::Attached(a) => {
2678 a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
2679 }
2680 }
2681 }
2682
2683 pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
2684 self.insert_container(self.len(), child)
2685 }
2686
2687 pub fn insert_container_with_txn<H: HandlerTrait>(
2688 &self,
2689 txn: &mut Transaction,
2690 pos: usize,
2691 child: H,
2692 ) -> LoroResult<H> {
2693 if pos > self.len() {
2694 return Err(LoroError::OutOfBound {
2695 pos,
2696 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2697 len: self.len(),
2698 });
2699 }
2700
2701 let inner = self.inner.try_attached_state()?;
2702 let id = txn.next_id();
2703 let container_id = ContainerID::new_normal(id, child.kind());
2704 let v = LoroValue::Container(container_id.clone());
2705 txn.apply_local_op(
2706 inner.container_idx,
2707 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
2708 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
2709 pos,
2710 }),
2711 EventHint::InsertList { len: 1, pos },
2712 &inner.doc,
2713 )?;
2714 let ans = child.attach(txn, inner, container_id)?;
2715 Ok(ans)
2716 }
2717
2718 pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
2719 match &self.inner {
2720 MaybeDetached::Detached(l) => {
2721 let mut list = l.lock().unwrap();
2722 list.value.drain(pos..pos + len);
2723 Ok(())
2724 }
2725 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
2726 }
2727 }
2728
2729 pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
2730 if len == 0 {
2731 return Ok(());
2732 }
2733
2734 if pos + len > self.len() {
2735 return Err(LoroError::OutOfBound {
2736 pos: pos + len,
2737 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2738 len: self.len(),
2739 });
2740 }
2741
2742 let inner = self.inner.try_attached_state()?;
2743 let ids: Vec<_> = inner.with_state(|state| {
2744 let list = state.as_list_state().unwrap();
2745 (pos..pos + len)
2746 .map(|i| list.get_id_at(i).unwrap())
2747 .collect()
2748 });
2749
2750 for id in ids.into_iter() {
2751 txn.apply_local_op(
2752 inner.container_idx,
2753 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
2754 id.id(),
2755 pos as isize,
2756 1,
2757 ))),
2758 EventHint::DeleteList(DeleteSpan::new(pos as isize, 1)),
2759 &inner.doc,
2760 )?;
2761 }
2762
2763 Ok(())
2764 }
2765
2766 pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
2767 match &self.inner {
2768 MaybeDetached::Detached(l) => {
2769 let list = l.lock().unwrap();
2770 let value = list.value.get(index).ok_or(LoroError::OutOfBound {
2771 pos: index,
2772 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2773 len: list.value.len(),
2774 })?;
2775 match value {
2776 ValueOrHandler::Handler(h) => Ok(h.clone()),
2777 _ => Err(LoroError::ArgErr(
2778 format!(
2779 "Expected container at index {}, but found {:?}",
2780 index, value
2781 )
2782 .into_boxed_str(),
2783 )),
2784 }
2785 }
2786 MaybeDetached::Attached(a) => {
2787 let Some(value) = a.with_state(|state| {
2788 state.as_list_state().as_ref().unwrap().get(index).cloned()
2789 }) else {
2790 return Err(LoroError::OutOfBound {
2791 pos: index,
2792 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2793 len: a.with_state(|state| state.as_list_state().unwrap().len()),
2794 });
2795 };
2796 match value {
2797 LoroValue::Container(id) => Ok(create_handler(a, id)),
2798 _ => Err(LoroError::ArgErr(
2799 format!(
2800 "Expected container at index {}, but found {:?}",
2801 index, value
2802 )
2803 .into_boxed_str(),
2804 )),
2805 }
2806 }
2807 }
2808 }
2809
2810 pub fn len(&self) -> usize {
2811 match &self.inner {
2812 MaybeDetached::Detached(l) => l.lock().unwrap().value.len(),
2813 MaybeDetached::Attached(a) => {
2814 a.with_state(|state| state.as_list_state().unwrap().len())
2815 }
2816 }
2817 }
2818
2819 pub fn is_empty(&self) -> bool {
2820 self.len() == 0
2821 }
2822
2823 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
2824 let inner = self.inner.try_attached_state()?;
2825 Ok(inner.with_doc_state(|state| {
2826 state.get_container_deep_value_with_id(inner.container_idx, None)
2827 }))
2828 }
2829
2830 pub fn get(&self, index: usize) -> Option<LoroValue> {
2831 match &self.inner {
2832 MaybeDetached::Detached(l) => l.lock().unwrap().value.get(index).map(|x| x.to_value()),
2833 MaybeDetached::Attached(a) => a.with_state(|state| {
2834 let a = state.as_list_state().unwrap();
2835 a.get(index).cloned()
2836 }),
2837 }
2838 }
2839
2840 pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
2842 match &self.inner {
2843 MaybeDetached::Detached(l) => {
2844 let l = l.lock().unwrap();
2845 l.value.get(index).cloned()
2846 }
2847 MaybeDetached::Attached(inner) => {
2848 let value =
2849 inner.with_state(|state| state.as_list_state().unwrap().get(index).cloned());
2850 match value {
2851 Some(LoroValue::Container(container_id)) => Some(ValueOrHandler::Handler(
2852 create_handler(inner, container_id.clone()),
2853 )),
2854 Some(value) => Some(ValueOrHandler::Value(value.clone())),
2855 None => None,
2856 }
2857 }
2858 }
2859 }
2860
2861 pub fn for_each<I>(&self, mut f: I)
2862 where
2863 I: FnMut(ValueOrHandler),
2864 {
2865 match &self.inner {
2866 MaybeDetached::Detached(l) => {
2867 let l = l.lock().unwrap();
2868 for v in l.value.iter() {
2869 f(v.clone())
2870 }
2871 }
2872 MaybeDetached::Attached(inner) => {
2873 let mut temp = vec![];
2874 inner.with_state(|state| {
2875 let a = state.as_list_state().unwrap();
2876 for v in a.iter() {
2877 match v {
2878 LoroValue::Container(c) => {
2879 temp.push(ValueOrHandler::Handler(create_handler(
2880 inner,
2881 c.clone(),
2882 )));
2883 }
2884 value => {
2885 temp.push(ValueOrHandler::Value(value.clone()));
2886 }
2887 }
2888 }
2889 });
2890 for v in temp.into_iter() {
2891 f(v);
2892 }
2893 }
2894 }
2895 }
2896
2897 pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
2898 match &self.inner {
2899 MaybeDetached::Detached(_) => None,
2900 MaybeDetached::Attached(a) => {
2901 let (id, len) = a.with_state(|s| {
2902 let l = s.as_list_state().unwrap();
2903 (l.get_id_at(pos), l.len())
2904 });
2905
2906 if len == 0 {
2907 return Some(Cursor {
2908 id: None,
2909 container: self.id(),
2910 side: if side == Side::Middle {
2911 Side::Left
2912 } else {
2913 side
2914 },
2915 origin_pos: 0,
2916 });
2917 }
2918
2919 if len <= pos {
2920 return Some(Cursor {
2921 id: None,
2922 container: self.id(),
2923 side: Side::Right,
2924 origin_pos: len,
2925 });
2926 }
2927
2928 let id = id?;
2929 Some(Cursor {
2930 id: Some(id.id()),
2931 container: self.id(),
2932 side,
2933 origin_pos: pos,
2934 })
2935 }
2936 }
2937 }
2938
2939 fn apply_delta(
2940 &self,
2941 delta: loro_delta::DeltaRope<
2942 loro_delta::array_vec::ArrayVec<ValueOrHandler, 8>,
2943 crate::event::ListDeltaMeta,
2944 >,
2945 on_container_remap: &mut dyn FnMut(ContainerID, ContainerID),
2946 ) -> LoroResult<()> {
2947 match &self.inner {
2948 MaybeDetached::Detached(_) => unimplemented!(),
2949 MaybeDetached::Attached(_) => {
2950 let mut index = 0;
2951 for item in delta.iter() {
2952 match item {
2953 loro_delta::DeltaItem::Retain { len, .. } => {
2954 index += len;
2955 }
2956 loro_delta::DeltaItem::Replace { value, delete, .. } => {
2957 if *delete > 0 {
2958 self.delete(index, *delete)?;
2959 }
2960
2961 for v in value.iter() {
2962 match v {
2963 ValueOrHandler::Value(LoroValue::Container(old_id)) => {
2964 let new_h = self.insert_container(
2965 index,
2966 Handler::new_unattached(old_id.container_type()),
2967 )?;
2968 let new_id = new_h.id();
2969 on_container_remap(old_id.clone(), new_id);
2970 }
2971 ValueOrHandler::Handler(h) => {
2972 let old_id = h.id();
2973 let new_h = self.insert_container(
2974 index,
2975 Handler::new_unattached(old_id.container_type()),
2976 )?;
2977 let new_id = new_h.id();
2978 on_container_remap(old_id, new_id);
2979 }
2980 ValueOrHandler::Value(v) => {
2981 self.insert(index, v.clone())?;
2982 }
2983 }
2984
2985 index += 1;
2986 }
2987 }
2988 }
2989 }
2990 }
2991 }
2992
2993 Ok(())
2994 }
2995
2996 pub fn is_deleted(&self) -> bool {
2997 match &self.inner {
2998 MaybeDetached::Detached(_) => false,
2999 MaybeDetached::Attached(a) => a.is_deleted(),
3000 }
3001 }
3002
3003 pub fn clear(&self) -> LoroResult<()> {
3004 match &self.inner {
3005 MaybeDetached::Detached(l) => {
3006 let mut l = l.lock().unwrap();
3007 l.value.clear();
3008 Ok(())
3009 }
3010 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
3011 }
3012 }
3013
3014 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
3015 self.delete_with_txn(txn, 0, self.len())
3016 }
3017
3018 pub fn get_id_at(&self, pos: usize) -> Option<ID> {
3019 match &self.inner {
3020 MaybeDetached::Detached(_) => None,
3021 MaybeDetached::Attached(a) => a.with_state(|state| {
3022 state
3023 .as_list_state()
3024 .unwrap()
3025 .get_id_at(pos)
3026 .map(|x| x.id())
3027 }),
3028 }
3029 }
3030}
3031
3032impl MovableListHandler {
3033 pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3034 match &self.inner {
3035 MaybeDetached::Detached(d) => {
3036 let mut d = d.lock().unwrap();
3037 if pos > d.value.len() {
3038 return Err(LoroError::OutOfBound {
3039 pos,
3040 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3041 len: d.value.len(),
3042 });
3043 }
3044 d.value.insert(pos, ValueOrHandler::Value(v.into()));
3045 Ok(())
3046 }
3047 MaybeDetached::Attached(a) => {
3048 a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
3049 }
3050 }
3051 }
3052
3053 #[instrument(skip_all)]
3054 pub fn insert_with_txn(
3055 &self,
3056 txn: &mut Transaction,
3057 pos: usize,
3058 v: LoroValue,
3059 ) -> LoroResult<()> {
3060 if pos > self.len() {
3061 return Err(LoroError::OutOfBound {
3062 pos,
3063 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3064 len: self.len(),
3065 });
3066 }
3067
3068 if v.is_container() {
3069 return Err(LoroError::ArgErr(
3070 INSERT_CONTAINER_VALUE_ARG_ERROR
3071 .to_string()
3072 .into_boxed_str(),
3073 ));
3074 }
3075
3076 let op_index = self.with_state(|state| {
3077 let list = state.as_movable_list_state().unwrap();
3078 Ok(list
3079 .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3080 .unwrap())
3081 })?;
3082
3083 let inner = self.inner.try_attached_state()?;
3084 txn.apply_local_op(
3085 inner.container_idx,
3086 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3087 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3088 pos: op_index,
3089 }),
3090 EventHint::InsertList { len: 1, pos },
3091 &inner.doc,
3092 )
3093 }
3094
3095 #[inline]
3096 pub fn mov(&self, from: usize, to: usize) -> LoroResult<()> {
3097 match &self.inner {
3098 MaybeDetached::Detached(d) => {
3099 let mut d = d.lock().unwrap();
3100 if from >= d.value.len() {
3101 return Err(LoroError::OutOfBound {
3102 pos: from,
3103 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3104 len: d.value.len(),
3105 });
3106 }
3107 if to >= d.value.len() {
3108 return Err(LoroError::OutOfBound {
3109 pos: to,
3110 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3111 len: d.value.len(),
3112 });
3113 }
3114 let v = d.value.remove(from);
3115 d.value.insert(to, v);
3116 Ok(())
3117 }
3118 MaybeDetached::Attached(a) => a.with_txn(|txn| self.move_with_txn(txn, from, to)),
3119 }
3120 }
3121
3122 #[instrument(skip_all)]
3124 pub fn move_with_txn(&self, txn: &mut Transaction, from: usize, to: usize) -> LoroResult<()> {
3125 if from == to {
3126 return Ok(());
3127 }
3128
3129 if from >= self.len() {
3130 return Err(LoroError::OutOfBound {
3131 pos: from,
3132 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3133 len: self.len(),
3134 });
3135 }
3136
3137 if to >= self.len() {
3138 return Err(LoroError::OutOfBound {
3139 pos: to,
3140 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3141 len: self.len(),
3142 });
3143 }
3144
3145 let (op_from, op_to, elem_id, value) = self.with_state(|state| {
3146 let list = state.as_movable_list_state().unwrap();
3147 let (elem_id, elem) = list
3148 .get_elem_at_given_pos(from, IndexType::ForUser)
3149 .unwrap();
3150 Ok((
3151 list.convert_index(from, IndexType::ForUser, IndexType::ForOp)
3152 .unwrap(),
3153 list.convert_index(to, IndexType::ForUser, IndexType::ForOp)
3154 .unwrap(),
3155 elem_id,
3156 elem.value().clone(),
3157 ))
3158 })?;
3159
3160 let inner = self.inner.try_attached_state()?;
3161 txn.apply_local_op(
3162 inner.container_idx,
3163 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Move {
3164 from: op_from as u32,
3165 to: op_to as u32,
3166 elem_id: elem_id.to_id(),
3167 }),
3168 EventHint::Move {
3169 value,
3170 from: from as u32,
3171 to: to as u32,
3172 },
3173 &inner.doc,
3174 )
3175 }
3176
3177 pub fn push(&self, v: LoroValue) -> LoroResult<()> {
3178 match &self.inner {
3179 MaybeDetached::Detached(d) => {
3180 let mut d = d.lock().unwrap();
3181 d.value.push(v.into());
3182 Ok(())
3183 }
3184 MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v)),
3185 }
3186 }
3187
3188 pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
3189 let pos = self.len();
3190 self.insert_with_txn(txn, pos, v)
3191 }
3192
3193 pub fn pop_(&self) -> LoroResult<Option<ValueOrHandler>> {
3194 match &self.inner {
3195 MaybeDetached::Detached(d) => {
3196 let mut d = d.lock().unwrap();
3197 Ok(d.value.pop())
3198 }
3199 MaybeDetached::Attached(a) => {
3200 let last = self.len() - 1;
3201 let ans = self.get_(last);
3202 a.with_txn(|txn| self.pop_with_txn(txn))?;
3203 Ok(ans)
3204 }
3205 }
3206 }
3207
3208 pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
3209 match &self.inner {
3210 MaybeDetached::Detached(a) => {
3211 let mut a = a.lock().unwrap();
3212 Ok(a.value.pop().map(|x| x.to_value()))
3213 }
3214 MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
3215 }
3216 }
3217
3218 pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
3219 let len = self.len();
3220 if len == 0 {
3221 return Ok(None);
3222 }
3223
3224 let v = self.get(len - 1);
3225 self.delete_with_txn(txn, len - 1, 1)?;
3226 Ok(v)
3227 }
3228
3229 pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3230 match &self.inner {
3231 MaybeDetached::Detached(d) => {
3232 let mut d = d.lock().unwrap();
3233 if pos > d.value.len() {
3234 return Err(LoroError::OutOfBound {
3235 pos,
3236 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3237 len: d.value.len(),
3238 });
3239 }
3240 d.value
3241 .insert(pos, ValueOrHandler::Handler(child.to_handler()));
3242 Ok(child)
3243 }
3244 MaybeDetached::Attached(a) => {
3245 a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
3246 }
3247 }
3248 }
3249
3250 pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
3251 self.insert_container(self.len(), child)
3252 }
3253
3254 pub fn insert_container_with_txn<H: HandlerTrait>(
3255 &self,
3256 txn: &mut Transaction,
3257 pos: usize,
3258 child: H,
3259 ) -> LoroResult<H> {
3260 if pos > self.len() {
3261 return Err(LoroError::OutOfBound {
3262 pos,
3263 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3264 len: self.len(),
3265 });
3266 }
3267
3268 let op_index = self.with_state(|state| {
3269 let list = state.as_movable_list_state().unwrap();
3270 Ok(list
3271 .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3272 .unwrap())
3273 })?;
3274
3275 let id = txn.next_id();
3276 let container_id = ContainerID::new_normal(id, child.kind());
3277 let v = LoroValue::Container(container_id.clone());
3278 let inner = self.inner.try_attached_state()?;
3279 txn.apply_local_op(
3280 inner.container_idx,
3281 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3282 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3283 pos: op_index,
3284 }),
3285 EventHint::InsertList { len: 1, pos },
3286 &inner.doc,
3287 )?;
3288 child.attach(txn, inner, container_id)
3289 }
3290
3291 pub fn set(&self, index: usize, value: impl Into<LoroValue>) -> LoroResult<()> {
3292 match &self.inner {
3293 MaybeDetached::Detached(d) => {
3294 let mut d = d.lock().unwrap();
3295 if index >= d.value.len() {
3296 return Err(LoroError::OutOfBound {
3297 pos: index,
3298 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3299 len: d.value.len(),
3300 });
3301 }
3302 d.value[index] = ValueOrHandler::Value(value.into());
3303 Ok(())
3304 }
3305 MaybeDetached::Attached(a) => {
3306 a.with_txn(|txn| self.set_with_txn(txn, index, value.into()))
3307 }
3308 }
3309 }
3310
3311 pub fn set_with_txn(
3312 &self,
3313 txn: &mut Transaction,
3314 index: usize,
3315 value: LoroValue,
3316 ) -> LoroResult<()> {
3317 if index >= self.len() {
3318 return Err(LoroError::OutOfBound {
3319 pos: index,
3320 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3321 len: self.len(),
3322 });
3323 }
3324
3325 let inner = self.inner.try_attached_state()?;
3326 let Some(elem_id) = self.with_state(|state| {
3327 let list = state.as_movable_list_state().unwrap();
3328 Ok(list.get_elem_id_at(index, IndexType::ForUser))
3329 })?
3330 else {
3331 unreachable!()
3332 };
3333
3334 let op = crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3335 elem_id: elem_id.to_id(),
3336 value: value.clone(),
3337 });
3338
3339 let hint = EventHint::SetList { index, value };
3340 txn.apply_local_op(inner.container_idx, op, hint, &inner.doc)
3341 }
3342
3343 pub fn set_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3344 match &self.inner {
3345 MaybeDetached::Detached(d) => {
3346 let mut d = d.lock().unwrap();
3347 d.value[pos] = ValueOrHandler::Handler(child.to_handler());
3348 Ok(child)
3349 }
3350 MaybeDetached::Attached(a) => {
3351 a.with_txn(|txn| self.set_container_with_txn(txn, pos, child))
3352 }
3353 }
3354 }
3355
3356 pub fn set_container_with_txn<H: HandlerTrait>(
3357 &self,
3358 txn: &mut Transaction,
3359 pos: usize,
3360 child: H,
3361 ) -> LoroResult<H> {
3362 let id = txn.next_id();
3363 let container_id = ContainerID::new_normal(id, child.kind());
3364 let v = LoroValue::Container(container_id.clone());
3365 let Some(elem_id) = self.with_state(|state| {
3366 let list = state.as_movable_list_state().unwrap();
3367 Ok(list.get_elem_id_at(pos, IndexType::ForUser))
3368 })?
3369 else {
3370 let len = self.len();
3371 if pos >= len {
3372 return Err(LoroError::OutOfBound {
3373 pos,
3374 len,
3375 info: "".into(),
3376 });
3377 } else {
3378 unreachable!()
3379 }
3380 };
3381 let inner = self.inner.try_attached_state()?;
3382 txn.apply_local_op(
3383 inner.container_idx,
3384 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3385 elem_id: elem_id.to_id(),
3386 value: v.clone(),
3387 }),
3388 EventHint::SetList {
3389 index: pos,
3390 value: v,
3391 },
3392 &inner.doc,
3393 )?;
3394
3395 child.attach(txn, inner, container_id)
3396 }
3397
3398 pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3399 match &self.inner {
3400 MaybeDetached::Detached(d) => {
3401 let mut d = d.lock().unwrap();
3402 d.value.drain(pos..pos + len);
3403 Ok(())
3404 }
3405 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
3406 }
3407 }
3408
3409 #[instrument(skip_all)]
3410 pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
3411 if len == 0 {
3412 return Ok(());
3413 }
3414
3415 if pos + len > self.len() {
3416 return Err(LoroError::OutOfBound {
3417 pos: pos + len,
3418 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3419 len: self.len(),
3420 });
3421 }
3422
3423 let (ids, new_poses) = self.with_state(|state| {
3424 let list = state.as_movable_list_state().unwrap();
3425 let ids: Vec<_> = (pos..pos + len)
3426 .map(|i| list.get_list_id_at(i, IndexType::ForUser).unwrap())
3427 .collect();
3428 let poses: Vec<_> = (pos..pos + len)
3429 .map(|user_index| {
3431 let op_index = list
3432 .convert_index(user_index, IndexType::ForUser, IndexType::ForOp)
3433 .unwrap();
3434 assert!(op_index >= user_index);
3435 op_index - (user_index - pos)
3436 })
3437 .collect();
3438 Ok((ids, poses))
3439 })?;
3440
3441 loro_common::info!(?pos, ?len, ?ids, ?new_poses, "delete_with_txn");
3442 let user_pos = pos;
3443 let inner = self.inner.try_attached_state()?;
3444 for (id, op_pos) in ids.into_iter().zip(new_poses.into_iter()) {
3445 txn.apply_local_op(
3446 inner.container_idx,
3447 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
3448 id,
3449 op_pos as isize,
3450 1,
3451 ))),
3452 EventHint::DeleteList(DeleteSpan::new(user_pos as isize, 1)),
3453 &inner.doc,
3454 )?;
3455 }
3456
3457 Ok(())
3458 }
3459
3460 pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
3461 match &self.inner {
3462 MaybeDetached::Detached(l) => {
3463 let list = l.lock().unwrap();
3464 let value = list.value.get(index).ok_or(LoroError::OutOfBound {
3465 pos: index,
3466 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3467 len: list.value.len(),
3468 })?;
3469 match value {
3470 ValueOrHandler::Handler(h) => Ok(h.clone()),
3471 _ => Err(LoroError::ArgErr(
3472 format!(
3473 "Expected container at index {}, but found {:?}",
3474 index, value
3475 )
3476 .into_boxed_str(),
3477 )),
3478 }
3479 }
3480 MaybeDetached::Attached(a) => {
3481 let Some(value) = a.with_state(|state| {
3482 state
3483 .as_movable_list_state()
3484 .as_ref()
3485 .unwrap()
3486 .get(index, IndexType::ForUser)
3487 .cloned()
3488 }) else {
3489 return Err(LoroError::OutOfBound {
3490 pos: index,
3491 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3492 len: a.with_state(|state| state.as_list_state().unwrap().len()),
3493 });
3494 };
3495 match value {
3496 LoroValue::Container(id) => Ok(create_handler(a, id)),
3497 _ => Err(LoroError::ArgErr(
3498 format!(
3499 "Expected container at index {}, but found {:?}",
3500 index, value
3501 )
3502 .into_boxed_str(),
3503 )),
3504 }
3505 }
3506 }
3507 }
3508
3509 pub fn len(&self) -> usize {
3510 match &self.inner {
3511 MaybeDetached::Detached(d) => {
3512 let d = d.lock().unwrap();
3513 d.value.len()
3514 }
3515 MaybeDetached::Attached(a) => {
3516 a.with_state(|state| state.as_movable_list_state().unwrap().len())
3517 }
3518 }
3519 }
3520
3521 pub fn is_empty(&self) -> bool {
3522 self.len() == 0
3523 }
3524
3525 pub fn get_deep_value_with_id(&self) -> LoroValue {
3526 let inner = self.inner.try_attached_state().unwrap();
3527 inner
3528 .doc
3529 .state
3530 .lock()
3531 .unwrap()
3532 .get_container_deep_value_with_id(inner.container_idx, None)
3533 }
3534
3535 pub fn get(&self, index: usize) -> Option<LoroValue> {
3536 match &self.inner {
3537 MaybeDetached::Detached(d) => {
3538 let d = d.lock().unwrap();
3539 d.value.get(index).map(|v| v.to_value())
3540 }
3541 MaybeDetached::Attached(a) => a.with_state(|state| {
3542 let a = state.as_movable_list_state().unwrap();
3543 a.get(index, IndexType::ForUser).cloned()
3544 }),
3545 }
3546 }
3547
3548 pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
3550 match &self.inner {
3551 MaybeDetached::Detached(d) => {
3552 let d = d.lock().unwrap();
3553 d.value.get(index).cloned()
3554 }
3555 MaybeDetached::Attached(m) => m.with_state(|state| {
3556 let a = state.as_movable_list_state().unwrap();
3557 match a.get(index, IndexType::ForUser) {
3558 Some(v) => {
3559 if let LoroValue::Container(c) = v {
3560 Some(ValueOrHandler::Handler(create_handler(m, c.clone())))
3561 } else {
3562 Some(ValueOrHandler::Value(v.clone()))
3563 }
3564 }
3565 None => None,
3566 }
3567 }),
3568 }
3569 }
3570
3571 pub fn for_each<I>(&self, mut f: I)
3572 where
3573 I: FnMut(ValueOrHandler),
3574 {
3575 match &self.inner {
3576 MaybeDetached::Detached(d) => {
3577 let d = d.lock().unwrap();
3578 for v in d.value.iter() {
3579 f(v.clone());
3580 }
3581 }
3582 MaybeDetached::Attached(m) => {
3583 let mut temp = vec![];
3584 m.with_state(|state| {
3585 let a = state.as_movable_list_state().unwrap();
3586 for v in a.iter() {
3587 match v {
3588 LoroValue::Container(c) => {
3589 temp.push(ValueOrHandler::Handler(create_handler(m, c.clone())));
3590 }
3591 value => {
3592 temp.push(ValueOrHandler::Value(value.clone()));
3593 }
3594 }
3595 }
3596 });
3597
3598 for v in temp.into_iter() {
3599 f(v);
3600 }
3601 }
3602 }
3603 }
3604
3605 pub fn log_internal_state(&self) -> String {
3606 match &self.inner {
3607 MaybeDetached::Detached(d) => {
3608 let d = d.lock().unwrap();
3609 format!("{:#?}", &d.value)
3610 }
3611 MaybeDetached::Attached(a) => a.with_state(|state| {
3612 let a = state.as_movable_list_state().unwrap();
3613 format!("{a:#?}")
3614 }),
3615 }
3616 }
3617
3618 pub fn new_detached() -> MovableListHandler {
3619 MovableListHandler {
3620 inner: MaybeDetached::new_detached(Default::default()),
3621 }
3622 }
3623
3624 pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
3625 match &self.inner {
3626 MaybeDetached::Detached(_) => None,
3627 MaybeDetached::Attached(inner) => {
3628 let (id, len) = inner.with_state(|s| {
3629 let l = s.as_movable_list_state().unwrap();
3630 (l.get_list_item_id_at(pos), l.len())
3631 });
3632
3633 if len == 0 {
3634 return Some(Cursor {
3635 id: None,
3636 container: self.id(),
3637 side: if side == Side::Middle {
3638 Side::Left
3639 } else {
3640 side
3641 },
3642 origin_pos: 0,
3643 });
3644 }
3645
3646 if len <= pos {
3647 return Some(Cursor {
3648 id: None,
3649 container: self.id(),
3650 side: Side::Right,
3651 origin_pos: len,
3652 });
3653 }
3654
3655 let id = id?;
3656 Some(Cursor {
3657 id: Some(id.id()),
3658 container: self.id(),
3659 side,
3660 origin_pos: pos,
3661 })
3662 }
3663 }
3664 }
3665
3666 pub(crate) fn op_pos_to_user_pos(&self, new_pos: usize) -> usize {
3667 match &self.inner {
3668 MaybeDetached::Detached(_) => new_pos,
3669 MaybeDetached::Attached(inner) => {
3670 let mut pos = new_pos;
3671 inner.with_state(|s| {
3672 let l = s.as_movable_list_state().unwrap();
3673 pos = l
3674 .convert_index(new_pos, IndexType::ForOp, IndexType::ForUser)
3675 .unwrap_or(l.len());
3676 });
3677 pos
3678 }
3679 }
3680 }
3681
3682 pub fn is_deleted(&self) -> bool {
3683 match &self.inner {
3684 MaybeDetached::Detached(_) => false,
3685 MaybeDetached::Attached(a) => a.is_deleted(),
3686 }
3687 }
3688
3689 pub fn clear(&self) -> LoroResult<()> {
3690 match &self.inner {
3691 MaybeDetached::Detached(d) => {
3692 let mut d = d.lock().unwrap();
3693 d.value.clear();
3694 Ok(())
3695 }
3696 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
3697 }
3698 }
3699
3700 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
3701 self.delete_with_txn(txn, 0, self.len())
3702 }
3703
3704 pub fn get_creator_at(&self, pos: usize) -> Option<PeerID> {
3705 match &self.inner {
3706 MaybeDetached::Detached(_) => None,
3707 MaybeDetached::Attached(a) => {
3708 a.with_state(|state| state.as_movable_list_state().unwrap().get_creator_at(pos))
3709 }
3710 }
3711 }
3712
3713 pub fn get_last_mover_at(&self, pos: usize) -> Option<PeerID> {
3714 match &self.inner {
3715 MaybeDetached::Detached(_) => None,
3716 MaybeDetached::Attached(a) => a.with_state(|state| {
3717 state
3718 .as_movable_list_state()
3719 .unwrap()
3720 .get_last_mover_at(pos)
3721 }),
3722 }
3723 }
3724
3725 pub fn get_last_editor_at(&self, pos: usize) -> Option<PeerID> {
3726 match &self.inner {
3727 MaybeDetached::Detached(_) => None,
3728 MaybeDetached::Attached(a) => a.with_state(|state| {
3729 state
3730 .as_movable_list_state()
3731 .unwrap()
3732 .get_last_editor_at(pos)
3733 }),
3734 }
3735 }
3736}
3737
3738impl MapHandler {
3739 pub fn new_detached() -> Self {
3743 Self {
3744 inner: MaybeDetached::new_detached(Default::default()),
3745 }
3746 }
3747
3748 pub fn insert(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
3749 match &self.inner {
3750 MaybeDetached::Detached(m) => {
3751 let mut m = m.lock().unwrap();
3752 m.value
3753 .insert(key.into(), ValueOrHandler::Value(value.into()));
3754 Ok(())
3755 }
3756 MaybeDetached::Attached(a) => {
3757 a.with_txn(|txn| self.insert_with_txn(txn, key, value.into()))
3758 }
3759 }
3760 }
3761
3762 fn insert_without_skipping(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
3764 match &self.inner {
3765 MaybeDetached::Detached(m) => {
3766 let mut m = m.lock().unwrap();
3767 m.value
3768 .insert(key.into(), ValueOrHandler::Value(value.into()));
3769 Ok(())
3770 }
3771 MaybeDetached::Attached(a) => a.with_txn(|txn| {
3772 let this = &self;
3773 let value = value.into();
3774 if let Some(_value) = value.as_container() {
3775 return Err(LoroError::ArgErr(
3776 INSERT_CONTAINER_VALUE_ARG_ERROR
3777 .to_string()
3778 .into_boxed_str(),
3779 ));
3780 }
3781
3782 let inner = this.inner.try_attached_state()?;
3783 txn.apply_local_op(
3784 inner.container_idx,
3785 crate::op::RawOpContent::Map(crate::container::map::MapSet {
3786 key: key.into(),
3787 value: Some(value.clone()),
3788 }),
3789 EventHint::Map {
3790 key: key.into(),
3791 value: Some(value.clone()),
3792 },
3793 &inner.doc,
3794 )
3795 }),
3796 }
3797 }
3798
3799 pub fn insert_with_txn(
3800 &self,
3801 txn: &mut Transaction,
3802 key: &str,
3803 value: LoroValue,
3804 ) -> LoroResult<()> {
3805 if let Some(_value) = value.as_container() {
3806 return Err(LoroError::ArgErr(
3807 INSERT_CONTAINER_VALUE_ARG_ERROR
3808 .to_string()
3809 .into_boxed_str(),
3810 ));
3811 }
3812
3813 if self.get(key).map(|x| x == value).unwrap_or(false) {
3814 return Ok(());
3816 }
3817
3818 let inner = self.inner.try_attached_state()?;
3819 txn.apply_local_op(
3820 inner.container_idx,
3821 crate::op::RawOpContent::Map(crate::container::map::MapSet {
3822 key: key.into(),
3823 value: Some(value.clone()),
3824 }),
3825 EventHint::Map {
3826 key: key.into(),
3827 value: Some(value.clone()),
3828 },
3829 &inner.doc,
3830 )
3831 }
3832
3833 pub fn insert_container<T: HandlerTrait>(&self, key: &str, handler: T) -> LoroResult<T> {
3834 match &self.inner {
3835 MaybeDetached::Detached(m) => {
3836 let mut m = m.lock().unwrap();
3837 let to_insert = handler.to_handler();
3838 m.value
3839 .insert(key.into(), ValueOrHandler::Handler(to_insert.clone()));
3840 Ok(handler)
3841 }
3842 MaybeDetached::Attached(a) => {
3843 a.with_txn(|txn| self.insert_container_with_txn(txn, key, handler))
3844 }
3845 }
3846 }
3847
3848 pub fn insert_container_with_txn<H: HandlerTrait>(
3849 &self,
3850 txn: &mut Transaction,
3851 key: &str,
3852 child: H,
3853 ) -> LoroResult<H> {
3854 let inner = self.inner.try_attached_state()?;
3855 let id = txn.next_id();
3856 let container_id = ContainerID::new_normal(id, child.kind());
3857 txn.apply_local_op(
3858 inner.container_idx,
3859 crate::op::RawOpContent::Map(crate::container::map::MapSet {
3860 key: key.into(),
3861 value: Some(LoroValue::Container(container_id.clone())),
3862 }),
3863 EventHint::Map {
3864 key: key.into(),
3865 value: Some(LoroValue::Container(container_id.clone())),
3866 },
3867 &inner.doc,
3868 )?;
3869
3870 child.attach(txn, inner, container_id)
3871 }
3872
3873 pub fn delete(&self, key: &str) -> LoroResult<()> {
3874 match &self.inner {
3875 MaybeDetached::Detached(m) => {
3876 let mut m = m.lock().unwrap();
3877 m.value.remove(key);
3878 Ok(())
3879 }
3880 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, key)),
3881 }
3882 }
3883
3884 pub fn delete_with_txn(&self, txn: &mut Transaction, key: &str) -> LoroResult<()> {
3885 let inner = self.inner.try_attached_state()?;
3886 txn.apply_local_op(
3887 inner.container_idx,
3888 crate::op::RawOpContent::Map(crate::container::map::MapSet {
3889 key: key.into(),
3890 value: None,
3891 }),
3892 EventHint::Map {
3893 key: key.into(),
3894 value: None,
3895 },
3896 &inner.doc,
3897 )
3898 }
3899
3900 pub fn for_each<I>(&self, mut f: I)
3901 where
3902 I: FnMut(&str, ValueOrHandler),
3903 {
3904 match &self.inner {
3905 MaybeDetached::Detached(m) => {
3906 let m = m.lock().unwrap();
3907 for (k, v) in m.value.iter() {
3908 f(k, v.clone());
3909 }
3910 }
3911 MaybeDetached::Attached(inner) => {
3912 let mut temp = vec![];
3913 inner.with_state(|state| {
3914 let a = state.as_map_state().unwrap();
3915 for (k, v) in a.iter() {
3916 if let Some(v) = &v.value {
3917 match v {
3918 LoroValue::Container(c) => {
3919 temp.push((
3920 k.to_string(),
3921 ValueOrHandler::Handler(create_handler(inner, c.clone())),
3922 ));
3923 }
3924 value => {
3925 temp.push((k.to_string(), ValueOrHandler::Value(value.clone())))
3926 }
3927 }
3928 }
3929 }
3930 });
3931
3932 for (k, v) in temp.into_iter() {
3933 f(&k, v.clone());
3934 }
3935 }
3936 }
3937 }
3938
3939 pub fn get_child_handler(&self, key: &str) -> LoroResult<Handler> {
3940 match &self.inner {
3941 MaybeDetached::Detached(m) => {
3942 let m = m.lock().unwrap();
3943 let value = m.value.get(key).unwrap();
3944 match value {
3945 ValueOrHandler::Value(v) => Err(LoroError::ArgErr(
3946 format!("Expected Handler but found {:?}", v).into_boxed_str(),
3947 )),
3948 ValueOrHandler::Handler(h) => Ok(h.clone()),
3949 }
3950 }
3951 MaybeDetached::Attached(inner) => {
3952 let container_id = inner.with_state(|state| {
3953 state
3954 .as_map_state()
3955 .as_ref()
3956 .unwrap()
3957 .get(key)
3958 .unwrap()
3959 .as_container()
3960 .unwrap()
3961 .clone()
3962 });
3963 Ok(create_handler(inner, container_id))
3964 }
3965 }
3966 }
3967
3968 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
3969 match &self.inner {
3970 MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
3971 method: "get_deep_value_with_id",
3972 }),
3973 MaybeDetached::Attached(inner) => Ok(inner.with_doc_state(|state| {
3974 state.get_container_deep_value_with_id(inner.container_idx, None)
3975 })),
3976 }
3977 }
3978
3979 pub fn get(&self, key: &str) -> Option<LoroValue> {
3980 match &self.inner {
3981 MaybeDetached::Detached(m) => {
3982 let m = m.lock().unwrap();
3983 m.value.get(key).map(|v| v.to_value())
3984 }
3985 MaybeDetached::Attached(inner) => {
3986 inner.with_state(|state| state.as_map_state().unwrap().get(key).cloned())
3987 }
3988 }
3989 }
3990
3991 pub fn get_(&self, key: &str) -> Option<ValueOrHandler> {
3993 match &self.inner {
3994 MaybeDetached::Detached(m) => {
3995 let m = m.lock().unwrap();
3996 m.value.get(key).cloned()
3997 }
3998 MaybeDetached::Attached(inner) => {
3999 let value =
4000 inner.with_state(|state| state.as_map_state().unwrap().get(key).cloned());
4001 match value {
4002 Some(LoroValue::Container(container_id)) => Some(ValueOrHandler::Handler(
4003 create_handler(inner, container_id.clone()),
4004 )),
4005 Some(value) => Some(ValueOrHandler::Value(value.clone())),
4006 None => None,
4007 }
4008 }
4009 }
4010 }
4011
4012 pub fn get_or_create_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4013 if let Some(ans) = self.get_(key) {
4014 if let ValueOrHandler::Handler(h) = ans {
4015 let kind = h.kind();
4016 return C::from_handler(h).ok_or_else(move || {
4017 LoroError::ArgErr(
4018 format!("Expected value type {} but found {:?}", child.kind(), kind)
4019 .into_boxed_str(),
4020 )
4021 });
4022 } else if let ValueOrHandler::Value(LoroValue::Null) = ans {
4023 } else {
4025 return Err(LoroError::ArgErr(
4026 format!("Expected value type {} but found {:?}", child.kind(), ans)
4027 .into_boxed_str(),
4028 ));
4029 }
4030 }
4031
4032 self.insert_container(key, child)
4033 }
4034
4035 pub fn contains_key(&self, key: &str) -> bool {
4036 self.get(key).is_some()
4037 }
4038
4039 pub fn len(&self) -> usize {
4040 match &self.inner {
4041 MaybeDetached::Detached(m) => m.lock().unwrap().value.len(),
4042 MaybeDetached::Attached(a) => a.with_state(|state| state.as_map_state().unwrap().len()),
4043 }
4044 }
4045
4046 pub fn is_empty(&self) -> bool {
4047 self.len() == 0
4048 }
4049
4050 pub fn is_deleted(&self) -> bool {
4051 match &self.inner {
4052 MaybeDetached::Detached(_) => false,
4053 MaybeDetached::Attached(a) => a.is_deleted(),
4054 }
4055 }
4056
4057 pub fn clear(&self) -> LoroResult<()> {
4058 match &self.inner {
4059 MaybeDetached::Detached(m) => {
4060 let mut m = m.lock().unwrap();
4061 m.value.clear();
4062 Ok(())
4063 }
4064 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4065 }
4066 }
4067
4068 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4069 let keys: Vec<InternalString> = self.inner.try_attached_state()?.with_state(|state| {
4070 state
4071 .as_map_state()
4072 .unwrap()
4073 .iter()
4074 .map(|(k, _)| k.clone())
4075 .collect()
4076 });
4077
4078 for key in keys {
4079 self.delete_with_txn(txn, &key)?;
4080 }
4081
4082 Ok(())
4083 }
4084
4085 pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
4086 let mut keys: Vec<InternalString> = Vec::with_capacity(self.len());
4087 match &self.inner {
4088 MaybeDetached::Detached(m) => {
4089 let m = m.lock().unwrap();
4090 keys = m.value.keys().map(|x| x.as_str().into()).collect();
4091 }
4092 MaybeDetached::Attached(a) => {
4093 a.with_state(|state| {
4094 for (k, v) in state.as_map_state().unwrap().iter() {
4095 if v.value.is_some() {
4096 keys.push(k.clone());
4097 }
4098 }
4099 });
4100 }
4101 }
4102
4103 keys.into_iter()
4104 }
4105
4106 pub fn values(&self) -> impl Iterator<Item = ValueOrHandler> + '_ {
4107 let mut values: Vec<ValueOrHandler> = Vec::with_capacity(self.len());
4108 match &self.inner {
4109 MaybeDetached::Detached(m) => {
4110 let m = m.lock().unwrap();
4111 values = m.value.values().cloned().collect();
4112 }
4113 MaybeDetached::Attached(a) => {
4114 a.with_state(|state| {
4115 for (_, v) in state.as_map_state().unwrap().iter() {
4116 let value = match &v.value {
4117 Some(LoroValue::Container(container_id)) => {
4118 ValueOrHandler::Handler(create_handler(a, container_id.clone()))
4119 }
4120 Some(value) => ValueOrHandler::Value(value.clone()),
4121 None => continue,
4122 };
4123 values.push(value);
4124 }
4125 });
4126 }
4127 }
4128
4129 values.into_iter()
4130 }
4131
4132 pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
4133 match &self.inner {
4134 MaybeDetached::Detached(_) => None,
4135 MaybeDetached::Attached(a) => a.with_state(|state| {
4136 let m = state.as_map_state().unwrap();
4137 m.get_last_edit_peer(key)
4138 }),
4139 }
4140 }
4141}
4142
4143fn with_txn<R>(doc: &LoroDoc, f: impl FnOnce(&mut Transaction) -> LoroResult<R>) -> LoroResult<R> {
4144 let txn = &doc.txn;
4145 let mut txn = txn.lock().unwrap();
4146 loop {
4147 if let Some(txn) = &mut *txn {
4148 return f(txn);
4149 } else if cfg!(target_arch = "wasm32") || !doc.can_edit() {
4150 return Err(LoroError::AutoCommitNotStarted);
4151 } else {
4152 drop(txn);
4153 #[cfg(loom)]
4154 loom::thread::yield_now();
4155 doc.start_auto_commit();
4156 txn = doc.txn.lock().unwrap();
4157 }
4158 }
4159}
4160
4161#[cfg(feature = "counter")]
4162pub mod counter {
4163
4164 use loro_common::LoroResult;
4165
4166 use crate::{
4167 txn::{EventHint, Transaction},
4168 HandlerTrait,
4169 };
4170
4171 use super::{create_handler, Handler, MaybeDetached};
4172
4173 #[derive(Clone)]
4174 pub struct CounterHandler {
4175 pub(super) inner: MaybeDetached<f64>,
4176 }
4177
4178 impl CounterHandler {
4179 pub fn new_detached() -> Self {
4180 Self {
4181 inner: MaybeDetached::new_detached(0.),
4182 }
4183 }
4184
4185 pub fn increment(&self, n: f64) -> LoroResult<()> {
4186 match &self.inner {
4187 MaybeDetached::Detached(d) => {
4188 let d = &mut d.lock().unwrap().value;
4189 *d += n;
4190 Ok(())
4191 }
4192 MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, n)),
4193 }
4194 }
4195
4196 pub fn decrement(&self, n: f64) -> LoroResult<()> {
4197 match &self.inner {
4198 MaybeDetached::Detached(d) => {
4199 let d = &mut d.lock().unwrap().value;
4200 *d -= n;
4201 Ok(())
4202 }
4203 MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, -n)),
4204 }
4205 }
4206
4207 fn increment_with_txn(&self, txn: &mut Transaction, n: f64) -> LoroResult<()> {
4208 let inner = self.inner.try_attached_state()?;
4209 txn.apply_local_op(
4210 inner.container_idx,
4211 crate::op::RawOpContent::Counter(n),
4212 EventHint::Counter(n),
4213 &inner.doc,
4214 )
4215 }
4216
4217 pub fn is_deleted(&self) -> bool {
4218 match &self.inner {
4219 MaybeDetached::Detached(_) => false,
4220 MaybeDetached::Attached(a) => a.is_deleted(),
4221 }
4222 }
4223
4224 pub fn clear(&self) -> LoroResult<()> {
4225 self.decrement(self.get_value().into_double().unwrap())
4226 }
4227 }
4228
4229 impl std::fmt::Debug for CounterHandler {
4230 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4231 match &self.inner {
4232 MaybeDetached::Detached(_) => write!(f, "CounterHandler Detached"),
4233 MaybeDetached::Attached(a) => write!(f, "CounterHandler {}", a.id),
4234 }
4235 }
4236 }
4237
4238 impl HandlerTrait for CounterHandler {
4239 fn is_attached(&self) -> bool {
4240 matches!(&self.inner, MaybeDetached::Attached(..))
4241 }
4242
4243 fn attached_handler(&self) -> Option<&crate::BasicHandler> {
4244 self.inner.attached_handler()
4245 }
4246
4247 fn get_value(&self) -> loro_common::LoroValue {
4248 match &self.inner {
4249 MaybeDetached::Detached(t) => {
4250 let t = t.lock().unwrap();
4251 t.value.into()
4252 }
4253 MaybeDetached::Attached(a) => a.get_value(),
4254 }
4255 }
4256
4257 fn get_deep_value(&self) -> loro_common::LoroValue {
4258 self.get_value()
4259 }
4260
4261 fn kind(&self) -> loro_common::ContainerType {
4262 loro_common::ContainerType::Counter
4263 }
4264
4265 fn to_handler(&self) -> super::Handler {
4266 Handler::Counter(self.clone())
4267 }
4268
4269 fn from_handler(h: super::Handler) -> Option<Self> {
4270 match h {
4271 Handler::Counter(x) => Some(x),
4272 _ => None,
4273 }
4274 }
4275
4276 fn attach(
4277 &self,
4278 txn: &mut crate::txn::Transaction,
4279 parent: &crate::BasicHandler,
4280 self_id: loro_common::ContainerID,
4281 ) -> loro_common::LoroResult<Self> {
4282 match &self.inner {
4283 MaybeDetached::Detached(v) => {
4284 let mut v = v.lock().unwrap();
4285 let inner = create_handler(parent, self_id);
4286 let c = inner.into_counter().unwrap();
4287
4288 c.increment_with_txn(txn, v.value)?;
4289
4290 v.attached = c.attached_handler().cloned();
4291 Ok(c)
4292 }
4293 MaybeDetached::Attached(a) => {
4294 let new_inner = create_handler(a, self_id);
4295 let ans = new_inner.into_counter().unwrap();
4296 let delta = *self.get_value().as_double().unwrap();
4297 ans.increment_with_txn(txn, delta)?;
4298 Ok(ans)
4299 }
4300 }
4301 }
4302
4303 fn get_attached(&self) -> Option<Self> {
4304 match &self.inner {
4305 MaybeDetached::Attached(a) => Some(Self {
4306 inner: MaybeDetached::Attached(a.clone()),
4307 }),
4308 MaybeDetached::Detached(_) => None,
4309 }
4310 }
4311
4312 fn doc(&self) -> Option<crate::LoroDoc> {
4313 match &self.inner {
4314 MaybeDetached::Detached(_) => None,
4315 MaybeDetached::Attached(a) => Some(a.doc()),
4316 }
4317 }
4318 }
4319}
4320
4321#[cfg(test)]
4322mod test {
4323
4324 use super::{HandlerTrait, TextDelta};
4325 use crate::cursor::PosType;
4326 use crate::loro::ExportMode;
4327 use crate::state::TreeParentId;
4328 use crate::version::Frontiers;
4329 use crate::LoroDoc;
4330 use crate::{fx_map, ToJson};
4331 use loro_common::{LoroValue, ID};
4332 use serde_json::json;
4333
4334 #[test]
4335 fn richtext_handler() {
4336 let loro = LoroDoc::new();
4337 loro.set_peer_id(1).unwrap();
4338 let loro2 = LoroDoc::new();
4339 loro2.set_peer_id(2).unwrap();
4340
4341 let mut txn = loro.txn().unwrap();
4342 let text = txn.get_text("hello");
4343 text.insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
4344 .unwrap();
4345 txn.commit().unwrap();
4346 let exported = loro.export(ExportMode::all_updates()).unwrap();
4347
4348 loro2.import(&exported).unwrap();
4349 let mut txn = loro2.txn().unwrap();
4350 let text = txn.get_text("hello");
4351 assert_eq!(&**text.get_value().as_string().unwrap(), "hello");
4352 text.insert_with_txn(&mut txn, 5, " world", PosType::Unicode)
4353 .unwrap();
4354 assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
4355 txn.commit().unwrap();
4356
4357 loro.import(&loro2.export(ExportMode::all_updates()).unwrap())
4358 .unwrap();
4359 let txn = loro.txn().unwrap();
4360 let text = txn.get_text("hello");
4361 assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
4362 txn.commit().unwrap();
4363
4364 loro.checkout(&Frontiers::from_id(ID::new(2, 1))).unwrap();
4366 assert_eq!(&**text.get_value().as_string().unwrap(), "hello w");
4367 }
4368
4369 #[test]
4370 fn richtext_handler_concurrent() {
4371 let loro = LoroDoc::new();
4372 let mut txn = loro.txn().unwrap();
4373 let handler = loro.get_text("richtext");
4374 handler
4375 .insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
4376 .unwrap();
4377 txn.commit().unwrap();
4378 for i in 0..100 {
4379 let new_loro = LoroDoc::new();
4380 new_loro
4381 .import(&loro.export(ExportMode::all_updates()).unwrap())
4382 .unwrap();
4383 let mut txn = new_loro.txn().unwrap();
4384 let handler = new_loro.get_text("richtext");
4385 handler
4386 .insert_with_txn(&mut txn, i % 5, &i.to_string(), PosType::Unicode)
4387 .unwrap();
4388 txn.commit().unwrap();
4389 loro.import(
4390 &new_loro
4391 .export(ExportMode::updates(&loro.oplog_vv()))
4392 .unwrap(),
4393 )
4394 .unwrap();
4395 }
4396 }
4397
4398 #[test]
4399 fn richtext_handler_mark() {
4400 let loro = LoroDoc::new_auto_commit();
4401 let handler = loro.get_text("richtext");
4402 handler.insert(0, "hello world", PosType::Unicode).unwrap();
4403 handler
4404 .mark(0, 5, "bold", true.into(), PosType::Event)
4405 .unwrap();
4406 loro.commit_then_renew();
4407
4408 let value = handler.get_richtext_value();
4410 assert_eq!(value[0]["insert"], "hello".into());
4411 let meta = value[0]["attributes"].as_map().unwrap();
4412 assert_eq!(meta.len(), 1);
4413 meta.get("bold").unwrap();
4414
4415 let loro2 = LoroDoc::new_auto_commit();
4416 loro2
4417 .import(&loro.export(ExportMode::all_updates()).unwrap())
4418 .unwrap();
4419 let handler2 = loro2.get_text("richtext");
4420 assert_eq!(&**handler2.get_value().as_string().unwrap(), "hello world");
4421
4422 let value = handler2.get_richtext_value();
4424 assert_eq!(value[0]["insert"], "hello".into());
4425 let meta = value[0]["attributes"].as_map().unwrap();
4426 assert_eq!(meta.len(), 1);
4427 meta.get("bold").unwrap();
4428
4429 {
4431 handler2.insert(5, " new", PosType::Unicode).unwrap();
4432 let value = handler2.get_richtext_value();
4433 assert_eq!(
4434 value.to_json_value(),
4435 serde_json::json!([
4436 {"insert": "hello new", "attributes": {"bold": true}},
4437 {"insert": " world"}
4438 ])
4439 );
4440 }
4441 }
4442
4443 #[test]
4444 fn richtext_snapshot() {
4445 let loro = LoroDoc::new();
4446 let mut txn = loro.txn().unwrap();
4447 let handler = loro.get_text("richtext");
4448 handler
4449 .insert_with_txn(&mut txn, 0, "hello world", PosType::Unicode)
4450 .unwrap();
4451 handler
4452 .mark_with_txn(&mut txn, 0, 5, "bold", true.into(), PosType::Event, false)
4453 .unwrap();
4454 txn.commit().unwrap();
4455
4456 let loro2 = LoroDoc::new();
4457 loro2
4458 .import(&loro.export(ExportMode::snapshot()).unwrap())
4459 .unwrap();
4460 let handler2 = loro2.get_text("richtext");
4461 assert_eq!(
4462 handler2.get_richtext_value().to_json_value(),
4463 serde_json::json!([
4464 {"insert": "hello", "attributes": {"bold": true}},
4465 {"insert": " world"}
4466 ])
4467 );
4468 }
4469
4470 #[test]
4471 fn tree_meta() {
4472 let loro = LoroDoc::new_auto_commit();
4473 loro.set_peer_id(1).unwrap();
4474 let tree = loro.get_tree("root");
4475 let id = tree.create(TreeParentId::Root).unwrap();
4476 let meta = tree.get_meta(id).unwrap();
4477 meta.insert("a", 123).unwrap();
4478 loro.commit_then_renew();
4479 let meta = tree.get_meta(id).unwrap();
4480 assert_eq!(meta.get("a").unwrap(), 123.into());
4481 assert_eq!(
4482 json!([{"parent":null,"meta":{"a":123},"id":"0@1","index":0,"children":[],"fractional_index":"80"}]),
4483 tree.get_deep_value().to_json_value()
4484 );
4485 let bytes = loro.export(ExportMode::snapshot()).unwrap();
4486 let loro2 = LoroDoc::new();
4487 loro2.import(&bytes).unwrap();
4488 }
4489
4490 #[test]
4491 fn tree_meta_event() {
4492 use std::sync::Arc;
4493 let loro = LoroDoc::new_auto_commit();
4494 let tree = loro.get_tree("root");
4495 let text = loro.get_text("text");
4496
4497 let id = tree.create(TreeParentId::Root).unwrap();
4498 let meta = tree.get_meta(id).unwrap();
4499 meta.insert("a", 1).unwrap();
4500 text.insert(0, "abc", PosType::Unicode).unwrap();
4501 let _id2 = tree.create(TreeParentId::Root).unwrap();
4502 meta.insert("b", 2).unwrap();
4503
4504 let loro2 = LoroDoc::new_auto_commit();
4505 let _g = loro2.subscribe_root(Arc::new(|e| {
4506 println!("{} {:?} ", e.event_meta.by, e.event_meta.diff)
4507 }));
4508 loro2
4509 .import(&loro.export(ExportMode::all_updates()).unwrap())
4510 .unwrap();
4511 assert_eq!(loro.get_deep_value(), loro2.get_deep_value());
4512 }
4513
4514 #[test]
4515 fn richtext_apply_delta() {
4516 let loro = LoroDoc::new_auto_commit();
4517 let text = loro.get_text("text");
4518 text.apply_delta(&[TextDelta::Insert {
4519 insert: "Hello World!".into(),
4520 attributes: None,
4521 }])
4522 .unwrap();
4523 dbg!(text.get_richtext_value());
4524 text.apply_delta(&[
4525 TextDelta::Retain {
4526 retain: 6,
4527 attributes: Some(fx_map!("italic".into() => loro_common::LoroValue::Bool(true))),
4528 },
4529 TextDelta::Insert {
4530 insert: "New ".into(),
4531 attributes: Some(fx_map!("bold".into() => loro_common::LoroValue::Bool(true))),
4532 },
4533 ])
4534 .unwrap();
4535 dbg!(text.get_richtext_value());
4536 loro.commit_then_renew();
4537 assert_eq!(
4538 text.get_richtext_value().to_json_value(),
4539 json!([
4540 {"insert": "Hello ", "attributes": {"italic": true}},
4541 {"insert": "New ", "attributes": {"bold": true}},
4542 {"insert": "World!"}
4543
4544 ])
4545 )
4546 }
4547
4548 #[test]
4549 fn richtext_apply_delta_marks_without_growth() {
4550 let loro = LoroDoc::new_auto_commit();
4551 let text = loro.get_text("text");
4552 text.insert(0, "abc", PosType::Unicode).unwrap();
4553
4554 text.apply_delta(&[TextDelta::Retain {
4555 retain: 3,
4556 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
4557 }])
4558 .unwrap();
4559 loro.commit_then_renew();
4560
4561 assert_eq!(text.to_string(), "abc");
4562 assert_eq!(
4563 text.get_richtext_value().to_json_value(),
4564 json!([{"insert": "abc", "attributes": {"bold": true}}])
4565 );
4566 }
4567
4568 #[test]
4569 fn richtext_apply_delta_grows_for_mark_gap() {
4570 let loro = LoroDoc::new_auto_commit();
4571 let text = loro.get_text("text");
4572
4573 text.apply_delta(&[TextDelta::Retain {
4574 retain: 1,
4575 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
4576 }])
4577 .unwrap();
4578 loro.commit_then_renew();
4579
4580 assert_eq!(text.to_string(), "\n");
4581 assert_eq!(
4582 text.get_richtext_value().to_json_value(),
4583 json!([{"insert": "\n", "attributes": {"bold": true}}])
4584 );
4585 }
4586
4587 #[test]
4588 fn richtext_apply_delta_ignores_empty_inserts() {
4589 let loro = LoroDoc::new_auto_commit();
4590 let text = loro.get_text("text");
4591 text.insert(0, "seed", PosType::Unicode).unwrap();
4592
4593 text.apply_delta(&[TextDelta::Insert {
4594 insert: "".into(),
4595 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
4596 }])
4597 .unwrap();
4598 loro.commit_then_renew();
4599
4600 assert_eq!(text.to_string(), "seed");
4601 assert_eq!(
4602 text.get_richtext_value().to_json_value(),
4603 json!([{"insert": "seed"}])
4604 );
4605 }
4606}