Skip to main content

loro_internal/
handler.rs

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, StyleKey, 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 REGULAR_CONTAINER_VALUE_ARG_ERROR: &str =
37    "Cannot use a LoroValue::Container as a regular value. To create a child container, use insert_container/set_container, or ensure_mergeable_* on maps for mergeable children";
38
39mod text_update;
40
41fn ensure_no_regular_container_value(value: &LoroValue) -> LoroResult<()> {
42    // Fast path: scalar values can never transitively hold a container, so we
43    // skip the heap allocation + traversal below. This is the common case on
44    // the per-op insert hot path (inserting numbers/strings/bools), where the
45    // previous unconditional `vec![value]` allocation showed up as a measurable
46    // regression.
47    if !matches!(
48        value,
49        LoroValue::Container(_) | LoroValue::List(_) | LoroValue::Map(_)
50    ) {
51        return Ok(());
52    }
53
54    let mut stack = vec![value];
55    while let Some(value) = stack.pop() {
56        match value {
57            LoroValue::Container(_) => {
58                return Err(LoroError::ArgErr(
59                    REGULAR_CONTAINER_VALUE_ARG_ERROR
60                        .to_string()
61                        .into_boxed_str(),
62                ));
63            }
64            LoroValue::List(list) => {
65                stack.extend(list.iter());
66            }
67            LoroValue::Map(map) => {
68                stack.extend(map.values());
69            }
70            LoroValue::Null
71            | LoroValue::Bool(_)
72            | LoroValue::Double(_)
73            | LoroValue::I64(_)
74            | LoroValue::Binary(_)
75            | LoroValue::String(_) => {}
76        }
77    }
78
79    Ok(())
80}
81
82fn checked_range_end(
83    pos: usize,
84    len: usize,
85    container_len: usize,
86    info: Box<str>,
87) -> LoroResult<usize> {
88    let end = pos.checked_add(len).ok_or_else(|| LoroError::OutOfBound {
89        pos: usize::MAX,
90        len: container_len,
91        info: info.clone(),
92    })?;
93    if end > container_len {
94        return Err(LoroError::OutOfBound {
95            pos: end,
96            len: container_len,
97            info,
98        });
99    }
100
101    Ok(end)
102}
103
104fn checked_delta_index_end(pos: usize, len: usize, container_len: usize) -> LoroResult<usize> {
105    pos.checked_add(len).ok_or_else(|| LoroError::OutOfBound {
106        pos: usize::MAX,
107        len: container_len,
108        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
109    })
110}
111
112pub trait HandlerTrait: Clone + Sized {
113    fn is_attached(&self) -> bool;
114    fn attached_handler(&self) -> Option<&BasicHandler>;
115    fn get_value(&self) -> LoroValue;
116    fn get_deep_value(&self) -> LoroValue;
117    fn kind(&self) -> ContainerType;
118    fn to_handler(&self) -> Handler;
119    fn from_handler(h: Handler) -> Option<Self>;
120    fn doc(&self) -> Option<LoroDoc>;
121    /// This method returns an attached handler.
122    fn attach(
123        &self,
124        txn: &mut Transaction,
125        parent: &BasicHandler,
126        self_id: ContainerID,
127    ) -> LoroResult<Self>;
128    /// If a detached container is attached, this method will return its corresponding attached handler.
129    fn get_attached(&self) -> Option<Self>;
130
131    fn parent(&self) -> Option<Handler> {
132        self.attached_handler().and_then(|x| x.parent())
133    }
134
135    fn idx(&self) -> ContainerIdx {
136        self.attached_handler()
137            .map(|x| x.container_idx)
138            .unwrap_or_else(|| {
139                ContainerIdx::from_index_and_type(ContainerIdx::INDEX_MASK, self.kind())
140            })
141    }
142
143    fn id(&self) -> ContainerID {
144        self.attached_handler()
145            .map(|x| x.id.clone())
146            .unwrap_or_else(|| ContainerID::new_normal(ID::NONE_ID, self.kind()))
147    }
148
149    fn with_state<R>(&self, f: impl FnOnce(&mut State) -> LoroResult<R>) -> LoroResult<R> {
150        let inner = self
151            .attached_handler()
152            .ok_or(LoroError::MisuseDetachedContainer {
153                method: "with_state",
154            })?;
155        let state = inner.doc.state.clone();
156        let mut guard = state.lock();
157        guard.with_state_mut(inner.container_idx, f)
158    }
159}
160
161fn create_handler(inner: &BasicHandler, id: ContainerID) -> Handler {
162    Handler::new_attached(id, inner.doc.clone())
163}
164
165fn value_to_value_or_handler(inner: &BasicHandler, value: LoroValue) -> ValueOrHandler {
166    match value {
167        LoroValue::Container(container_id) => {
168            ValueOrHandler::Handler(create_handler(inner, container_id))
169        }
170        value => ValueOrHandler::Value(value),
171    }
172}
173
174/// Flatten attributes that allow overlap
175#[derive(Clone, Debug)]
176pub struct BasicHandler {
177    id: ContainerID,
178    container_idx: ContainerIdx,
179    doc: LoroDoc,
180}
181
182struct DetachedInner<T> {
183    value: T,
184    /// If the handler attached later, this field will be filled.
185    attached: Option<BasicHandler>,
186}
187
188impl<T> DetachedInner<T> {
189    fn new(v: T) -> Self {
190        Self {
191            value: v,
192            attached: None,
193        }
194    }
195}
196
197enum MaybeDetached<T> {
198    Detached(Arc<Mutex<DetachedInner<T>>>),
199    Attached(BasicHandler),
200}
201
202impl<T> Clone for MaybeDetached<T> {
203    fn clone(&self) -> Self {
204        match self {
205            MaybeDetached::Detached(a) => MaybeDetached::Detached(Arc::clone(a)),
206            MaybeDetached::Attached(a) => MaybeDetached::Attached(a.clone()),
207        }
208    }
209}
210
211impl<T> MaybeDetached<T> {
212    fn new_detached(v: T) -> Self {
213        MaybeDetached::Detached(Arc::new(Mutex::new(DetachedInner::new(v))))
214    }
215
216    fn is_attached(&self) -> bool {
217        match self {
218            MaybeDetached::Detached(_) => false,
219            MaybeDetached::Attached(_) => true,
220        }
221    }
222
223    fn attached_handler(&self) -> Option<&BasicHandler> {
224        match self {
225            MaybeDetached::Detached(_) => None,
226            MaybeDetached::Attached(a) => Some(a),
227        }
228    }
229
230    fn try_attached_state(&self) -> LoroResult<&BasicHandler> {
231        match self {
232            MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
233                method: "inner_state",
234            }),
235            MaybeDetached::Attached(a) => Ok(a),
236        }
237    }
238}
239
240impl<T> From<BasicHandler> for MaybeDetached<T> {
241    fn from(a: BasicHandler) -> Self {
242        MaybeDetached::Attached(a)
243    }
244}
245
246impl BasicHandler {
247    pub(crate) fn doc(&self) -> LoroDoc {
248        self.doc.clone()
249    }
250
251    #[inline]
252    fn with_doc_state<R>(&self, f: impl FnOnce(&mut DocState) -> R) -> R {
253        let state = self.doc.state.clone();
254        let mut guard = state.lock();
255        f(&mut guard)
256    }
257
258    fn with_txn<R>(
259        &self,
260        f: impl FnOnce(&mut Transaction) -> Result<R, LoroError>,
261    ) -> Result<R, LoroError> {
262        with_txn(&self.doc, f)
263    }
264
265    fn get_parent(&self) -> Option<Handler> {
266        let parent_idx = self.doc.arena.get_parent(self.container_idx)?;
267        let parent_id = self.doc.arena.get_container_id(parent_idx).unwrap();
268        {
269            let kind = parent_id.container_type();
270            let handler = BasicHandler {
271                container_idx: parent_idx,
272                id: parent_id,
273                doc: self.doc.clone(),
274            };
275
276            Some(match kind {
277                ContainerType::Map => Handler::Map(MapHandler {
278                    inner: handler.into(),
279                }),
280                ContainerType::List => Handler::List(ListHandler {
281                    inner: handler.into(),
282                }),
283                ContainerType::Tree => Handler::Tree(TreeHandler {
284                    inner: handler.into(),
285                }),
286                ContainerType::Text => Handler::Text(TextHandler {
287                    inner: handler.into(),
288                }),
289                ContainerType::MovableList => Handler::MovableList(MovableListHandler {
290                    inner: handler.into(),
291                }),
292                #[cfg(feature = "counter")]
293                ContainerType::Counter => Handler::Counter(counter::CounterHandler {
294                    inner: handler.into(),
295                }),
296                ContainerType::Unknown(_) => unreachable!(),
297            })
298        }
299    }
300
301    pub fn get_value(&self) -> LoroValue {
302        self.doc.state.lock().get_value_by_idx(self.container_idx)
303    }
304
305    pub fn get_deep_value(&self) -> LoroValue {
306        self.doc
307            .state
308            .lock()
309            .get_container_deep_value(self.container_idx)
310    }
311
312    fn with_state<R>(&self, f: impl FnOnce(&mut State) -> R) -> R {
313        let mut guard = self.doc.state.lock();
314        guard.with_state_mut(self.container_idx, f)
315    }
316
317    pub fn parent(&self) -> Option<Handler> {
318        self.get_parent()
319    }
320
321    fn is_deleted(&self) -> bool {
322        self.doc.state.lock().is_deleted(self.container_idx)
323    }
324
325    fn has_decoded_state(&self) -> bool {
326        self.with_doc_state(|state| state.has_decoded_container_state(self.container_idx))
327    }
328}
329
330/// Flatten attributes that allow overlap
331#[derive(Clone)]
332pub struct TextHandler {
333    inner: MaybeDetached<RichtextState>,
334}
335
336impl HandlerTrait for TextHandler {
337    fn to_handler(&self) -> Handler {
338        Handler::Text(self.clone())
339    }
340
341    fn attach(
342        &self,
343        txn: &mut Transaction,
344        parent: &BasicHandler,
345        self_id: ContainerID,
346    ) -> LoroResult<Self> {
347        match &self.inner {
348            MaybeDetached::Detached(t) => {
349                let mut t = t.lock();
350                let inner = create_handler(parent, self_id);
351                let text = inner.into_text().unwrap();
352                let mut delta: Vec<TextDelta> = Vec::new();
353                for span in t.value.iter() {
354                    delta.push(TextDelta::Insert {
355                        insert: span.text.to_string(),
356                        attributes: span.attributes.to_option_map(),
357                    });
358                }
359
360                text.apply_delta_with_txn(txn, &delta)?;
361                t.attached = text.attached_handler().cloned();
362                Ok(text)
363            }
364            MaybeDetached::Attached(a) => {
365                let new_inner = create_handler(a, self_id);
366                let ans = new_inner.into_text().unwrap();
367
368                let delta = self.get_delta();
369                ans.apply_delta_with_txn(txn, &delta)?;
370                Ok(ans)
371            }
372        }
373    }
374
375    fn attached_handler(&self) -> Option<&BasicHandler> {
376        self.inner.attached_handler()
377    }
378
379    fn get_value(&self) -> LoroValue {
380        match &self.inner {
381            MaybeDetached::Detached(t) => {
382                let t = t.lock();
383                LoroValue::String((t.value.to_string()).into())
384            }
385            MaybeDetached::Attached(a) => a.get_value(),
386        }
387    }
388
389    fn get_deep_value(&self) -> LoroValue {
390        self.get_value()
391    }
392
393    fn is_attached(&self) -> bool {
394        matches!(&self.inner, MaybeDetached::Attached(..))
395    }
396
397    fn kind(&self) -> ContainerType {
398        ContainerType::Text
399    }
400
401    fn get_attached(&self) -> Option<Self> {
402        match &self.inner {
403            MaybeDetached::Detached(d) => d.lock().attached.clone().map(|x| Self {
404                inner: MaybeDetached::Attached(x),
405            }),
406            MaybeDetached::Attached(_a) => Some(self.clone()),
407        }
408    }
409
410    fn from_handler(h: Handler) -> Option<Self> {
411        match h {
412            Handler::Text(x) => Some(x),
413            _ => None,
414        }
415    }
416
417    fn doc(&self) -> Option<LoroDoc> {
418        match &self.inner {
419            MaybeDetached::Detached(_) => None,
420            MaybeDetached::Attached(a) => Some(a.doc()),
421        }
422    }
423}
424
425impl std::fmt::Debug for TextHandler {
426    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427        match &self.inner {
428            MaybeDetached::Detached(_) => {
429                write!(f, "TextHandler(Unattached)")
430            }
431            MaybeDetached::Attached(a) => {
432                write!(f, "TextHandler({:?})", &a.id)
433            }
434        }
435    }
436}
437
438#[derive(Debug, Clone, EnumAsInner, Deserialize, Serialize, PartialEq)]
439#[serde(untagged)]
440pub enum TextDelta {
441    Retain {
442        retain: usize,
443        attributes: Option<FxHashMap<String, LoroValue>>,
444    },
445    Insert {
446        insert: String,
447        attributes: Option<FxHashMap<String, LoroValue>>,
448    },
449    Delete {
450        delete: usize,
451    },
452}
453
454impl TextDelta {
455    pub fn from_text_diff<'a>(diff: impl Iterator<Item = &'a TextDiffItem>) -> Vec<TextDelta> {
456        let mut ans = Vec::with_capacity(diff.size_hint().0);
457        for iter in diff {
458            match iter {
459                loro_delta::DeltaItem::Retain { len, attr } => {
460                    ans.push(TextDelta::Retain {
461                        retain: *len,
462                        attributes: if attr.0.is_empty() {
463                            None
464                        } else {
465                            Some(attr.0.clone())
466                        },
467                    });
468                }
469                loro_delta::DeltaItem::Replace {
470                    value,
471                    attr,
472                    delete,
473                } => {
474                    if value.rle_len() > 0 {
475                        ans.push(TextDelta::Insert {
476                            insert: value.to_string(),
477                            attributes: if attr.0.is_empty() {
478                                None
479                            } else {
480                                Some(attr.0.clone())
481                            },
482                        });
483                    }
484                    if *delete > 0 {
485                        ans.push(TextDelta::Delete { delete: *delete });
486                    }
487                }
488            }
489        }
490
491        ans
492    }
493
494    pub fn into_text_diff(vec: impl Iterator<Item = Self>) -> TextDiff {
495        let mut delta = TextDiff::new();
496        for item in vec {
497            match item {
498                TextDelta::Retain { retain, attributes } => {
499                    delta.push_retain(retain, TextMeta(attributes.unwrap_or_default().clone()));
500                }
501                TextDelta::Insert { insert, attributes } => {
502                    delta.push_insert(
503                        StringSlice::from(insert.as_str()),
504                        TextMeta(attributes.unwrap_or_default()),
505                    );
506                }
507                TextDelta::Delete { delete } => {
508                    delta.push_delete(delete);
509                }
510            }
511        }
512
513        delta
514    }
515}
516
517impl From<&DeltaItem<StringSlice, StyleMeta>> for TextDelta {
518    fn from(value: &DeltaItem<StringSlice, StyleMeta>) -> Self {
519        match value {
520            crate::delta::DeltaItem::Retain { retain, attributes } => TextDelta::Retain {
521                retain: *retain,
522                attributes: attributes.to_option_map(),
523            },
524            crate::delta::DeltaItem::Insert { insert, attributes } => TextDelta::Insert {
525                insert: insert.to_string(),
526                attributes: attributes.to_option_map(),
527            },
528            crate::delta::DeltaItem::Delete {
529                delete,
530                attributes: _,
531            } => TextDelta::Delete { delete: *delete },
532        }
533    }
534}
535
536#[derive(Clone)]
537pub struct MapHandler {
538    inner: MaybeDetached<FxHashMap<String, ValueOrHandler>>,
539}
540
541impl HandlerTrait for MapHandler {
542    fn is_attached(&self) -> bool {
543        matches!(&self.inner, MaybeDetached::Attached(..))
544    }
545
546    fn attached_handler(&self) -> Option<&BasicHandler> {
547        match &self.inner {
548            MaybeDetached::Detached(_) => None,
549            MaybeDetached::Attached(a) => Some(a),
550        }
551    }
552
553    fn get_value(&self) -> LoroValue {
554        match &self.inner {
555            MaybeDetached::Detached(m) => {
556                let m = m.lock();
557                let mut map = FxHashMap::default();
558                for (k, v) in m.value.iter() {
559                    map.insert(k.to_string(), v.to_value());
560                }
561                LoroValue::Map(map.into())
562            }
563            MaybeDetached::Attached(a) => a.get_value(),
564        }
565    }
566
567    fn get_deep_value(&self) -> LoroValue {
568        match &self.inner {
569            MaybeDetached::Detached(m) => {
570                let m = m.lock();
571                let mut map = FxHashMap::default();
572                for (k, v) in m.value.iter() {
573                    map.insert(k.to_string(), v.to_deep_value());
574                }
575                LoroValue::Map(map.into())
576            }
577            MaybeDetached::Attached(a) => a.get_deep_value(),
578        }
579    }
580
581    fn kind(&self) -> ContainerType {
582        ContainerType::Map
583    }
584
585    fn to_handler(&self) -> Handler {
586        Handler::Map(self.clone())
587    }
588
589    fn attach(
590        &self,
591        txn: &mut Transaction,
592        parent: &BasicHandler,
593        self_id: ContainerID,
594    ) -> LoroResult<Self> {
595        match &self.inner {
596            MaybeDetached::Detached(m) => {
597                let mut m = m.lock();
598                let inner = create_handler(parent, self_id);
599                let map = inner.into_map().unwrap();
600                for (k, v) in m.value.iter() {
601                    match v {
602                        ValueOrHandler::Value(v) => {
603                            map.insert_with_txn(txn, k, v.clone())?;
604                        }
605                        ValueOrHandler::Handler(h) => {
606                            map.insert_container_with_txn(txn, k, h.clone())?;
607                        }
608                    }
609                }
610                m.attached = map.attached_handler().cloned();
611                Ok(map)
612            }
613            MaybeDetached::Attached(a) => {
614                let new_inner = create_handler(a, self_id);
615                let ans = new_inner.into_map().unwrap();
616
617                for (k, v) in self.get_value().into_map().unwrap().iter() {
618                    if let LoroValue::Container(id) = v {
619                        ans.insert_container_with_txn(txn, k, create_handler(a, id.clone()))?;
620                    } else {
621                        ans.insert_with_txn(txn, k, v.clone())?;
622                    }
623                }
624
625                Ok(ans)
626            }
627        }
628    }
629
630    fn get_attached(&self) -> Option<Self> {
631        match &self.inner {
632            MaybeDetached::Detached(d) => d.lock().attached.clone().map(|x| Self {
633                inner: MaybeDetached::Attached(x),
634            }),
635            MaybeDetached::Attached(_a) => Some(self.clone()),
636        }
637    }
638
639    fn from_handler(h: Handler) -> Option<Self> {
640        match h {
641            Handler::Map(x) => Some(x),
642            _ => None,
643        }
644    }
645
646    fn doc(&self) -> Option<LoroDoc> {
647        match &self.inner {
648            MaybeDetached::Detached(_) => None,
649            MaybeDetached::Attached(a) => Some(a.doc()),
650        }
651    }
652}
653
654impl std::fmt::Debug for MapHandler {
655    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
656        match &self.inner {
657            MaybeDetached::Detached(_) => write!(f, "MapHandler Detached"),
658            MaybeDetached::Attached(a) => write!(f, "MapHandler {}", a.id),
659        }
660    }
661}
662
663#[derive(Clone)]
664pub struct ListHandler {
665    inner: MaybeDetached<Vec<ValueOrHandler>>,
666}
667
668#[derive(Clone)]
669pub struct MovableListHandler {
670    inner: MaybeDetached<Vec<ValueOrHandler>>,
671}
672
673impl HandlerTrait for MovableListHandler {
674    fn is_attached(&self) -> bool {
675        matches!(&self.inner, MaybeDetached::Attached(..))
676    }
677
678    fn attached_handler(&self) -> Option<&BasicHandler> {
679        match &self.inner {
680            MaybeDetached::Detached(_) => None,
681            MaybeDetached::Attached(a) => Some(a),
682        }
683    }
684
685    fn get_value(&self) -> LoroValue {
686        match &self.inner {
687            MaybeDetached::Detached(a) => {
688                let a = a.lock();
689                LoroValue::List(a.value.iter().map(|v| v.to_value()).collect())
690            }
691            MaybeDetached::Attached(a) => a.get_value(),
692        }
693    }
694
695    fn get_deep_value(&self) -> LoroValue {
696        match &self.inner {
697            MaybeDetached::Detached(a) => {
698                let a = a.lock();
699                LoroValue::List(a.value.iter().map(|v| v.to_deep_value()).collect())
700            }
701            MaybeDetached::Attached(a) => a.get_deep_value(),
702        }
703    }
704
705    fn kind(&self) -> ContainerType {
706        ContainerType::MovableList
707    }
708
709    fn to_handler(&self) -> Handler {
710        Handler::MovableList(self.clone())
711    }
712
713    fn from_handler(h: Handler) -> Option<Self> {
714        match h {
715            Handler::MovableList(x) => Some(x),
716            _ => None,
717        }
718    }
719
720    fn attach(
721        &self,
722        txn: &mut Transaction,
723        parent: &BasicHandler,
724        self_id: ContainerID,
725    ) -> LoroResult<Self> {
726        match &self.inner {
727            MaybeDetached::Detached(l) => {
728                let mut l = l.lock();
729                let inner = create_handler(parent, self_id);
730                let list = inner.into_movable_list().unwrap();
731                for (index, v) in l.value.iter().enumerate() {
732                    match v {
733                        ValueOrHandler::Value(v) => {
734                            list.insert_with_txn(txn, index, v.clone())?;
735                        }
736                        ValueOrHandler::Handler(h) => {
737                            list.insert_container_with_txn(txn, index, h.clone())?;
738                        }
739                    }
740                }
741                l.attached = list.attached_handler().cloned();
742                Ok(list)
743            }
744            MaybeDetached::Attached(a) => {
745                let new_inner = create_handler(a, self_id);
746                let ans = new_inner.into_movable_list().unwrap();
747
748                for (i, v) in self.get_value().into_list().unwrap().iter().enumerate() {
749                    if let LoroValue::Container(id) = v {
750                        ans.insert_container_with_txn(txn, i, create_handler(a, id.clone()))?;
751                    } else {
752                        ans.insert_with_txn(txn, i, v.clone())?;
753                    }
754                }
755
756                Ok(ans)
757            }
758        }
759    }
760
761    fn get_attached(&self) -> Option<Self> {
762        match &self.inner {
763            MaybeDetached::Detached(d) => d.lock().attached.clone().map(|x| Self {
764                inner: MaybeDetached::Attached(x),
765            }),
766            MaybeDetached::Attached(_a) => Some(self.clone()),
767        }
768    }
769
770    fn doc(&self) -> Option<LoroDoc> {
771        match &self.inner {
772            MaybeDetached::Detached(_) => None,
773            MaybeDetached::Attached(a) => Some(a.doc()),
774        }
775    }
776}
777
778impl std::fmt::Debug for MovableListHandler {
779    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
780        write!(f, "MovableListHandler {}", self.id())
781    }
782}
783
784impl std::fmt::Debug for ListHandler {
785    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
786        match &self.inner {
787            MaybeDetached::Detached(_) => write!(f, "ListHandler Detached"),
788            MaybeDetached::Attached(a) => write!(f, "ListHandler {}", a.id),
789        }
790    }
791}
792
793impl HandlerTrait for ListHandler {
794    fn is_attached(&self) -> bool {
795        self.inner.is_attached()
796    }
797
798    fn attached_handler(&self) -> Option<&BasicHandler> {
799        self.inner.attached_handler()
800    }
801
802    fn get_value(&self) -> LoroValue {
803        match &self.inner {
804            MaybeDetached::Detached(a) => {
805                let a = a.lock();
806                LoroValue::List(a.value.iter().map(|v| v.to_value()).collect())
807            }
808            MaybeDetached::Attached(a) => a.get_value(),
809        }
810    }
811
812    fn get_deep_value(&self) -> LoroValue {
813        match &self.inner {
814            MaybeDetached::Detached(a) => {
815                let a = a.lock();
816                LoroValue::List(a.value.iter().map(|v| v.to_deep_value()).collect())
817            }
818            MaybeDetached::Attached(a) => a.get_deep_value(),
819        }
820    }
821
822    fn kind(&self) -> ContainerType {
823        ContainerType::List
824    }
825
826    fn to_handler(&self) -> Handler {
827        Handler::List(self.clone())
828    }
829
830    fn attach(
831        &self,
832        txn: &mut Transaction,
833        parent: &BasicHandler,
834        self_id: ContainerID,
835    ) -> LoroResult<Self> {
836        match &self.inner {
837            MaybeDetached::Detached(l) => {
838                let mut l = l.lock();
839                let inner = create_handler(parent, self_id);
840                let list = inner.into_list().unwrap();
841                for (index, v) in l.value.iter().enumerate() {
842                    match v {
843                        ValueOrHandler::Value(v) => {
844                            list.insert_with_txn(txn, index, v.clone())?;
845                        }
846                        ValueOrHandler::Handler(h) => {
847                            list.insert_container_with_txn(txn, index, h.clone())?;
848                        }
849                    }
850                }
851                l.attached = list.attached_handler().cloned();
852                Ok(list)
853            }
854            MaybeDetached::Attached(a) => {
855                let new_inner = create_handler(a, self_id);
856                let ans = new_inner.into_list().unwrap();
857
858                for (i, v) in self.get_value().into_list().unwrap().iter().enumerate() {
859                    if let LoroValue::Container(id) = v {
860                        ans.insert_container_with_txn(txn, i, create_handler(a, id.clone()))?;
861                    } else {
862                        ans.insert_with_txn(txn, i, v.clone())?;
863                    }
864                }
865
866                Ok(ans)
867            }
868        }
869    }
870
871    fn get_attached(&self) -> Option<Self> {
872        match &self.inner {
873            MaybeDetached::Detached(d) => d.lock().attached.clone().map(|x| Self {
874                inner: MaybeDetached::Attached(x),
875            }),
876            MaybeDetached::Attached(_a) => Some(self.clone()),
877        }
878    }
879
880    fn from_handler(h: Handler) -> Option<Self> {
881        match h {
882            Handler::List(x) => Some(x),
883            _ => None,
884        }
885    }
886
887    fn doc(&self) -> Option<LoroDoc> {
888        match &self.inner {
889            MaybeDetached::Detached(_) => None,
890            MaybeDetached::Attached(a) => Some(a.doc()),
891        }
892    }
893}
894
895#[derive(Clone)]
896pub struct UnknownHandler {
897    inner: BasicHandler,
898}
899
900impl Debug for UnknownHandler {
901    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
902        write!(f, "UnknownHandler")
903    }
904}
905
906impl UnknownHandler {
907    pub fn is_deleted(&self) -> bool {
908        self.inner.is_deleted()
909    }
910}
911
912impl HandlerTrait for UnknownHandler {
913    fn is_attached(&self) -> bool {
914        true
915    }
916
917    fn attached_handler(&self) -> Option<&BasicHandler> {
918        Some(&self.inner)
919    }
920
921    fn get_value(&self) -> LoroValue {
922        todo!()
923    }
924
925    fn get_deep_value(&self) -> LoroValue {
926        todo!()
927    }
928
929    fn kind(&self) -> ContainerType {
930        self.inner.id.container_type()
931    }
932
933    fn to_handler(&self) -> Handler {
934        Handler::Unknown(self.clone())
935    }
936
937    fn from_handler(h: Handler) -> Option<Self> {
938        match h {
939            Handler::Unknown(x) => Some(x),
940            _ => None,
941        }
942    }
943
944    fn attach(
945        &self,
946        _txn: &mut Transaction,
947        _parent: &BasicHandler,
948        self_id: ContainerID,
949    ) -> LoroResult<Self> {
950        let new_inner = create_handler(&self.inner, self_id);
951        let ans = new_inner.into_unknown().unwrap();
952        Ok(ans)
953    }
954
955    fn get_attached(&self) -> Option<Self> {
956        Some(self.clone())
957    }
958
959    fn doc(&self) -> Option<LoroDoc> {
960        Some(self.inner.doc())
961    }
962}
963
964#[derive(Clone, EnumAsInner, Debug)]
965pub enum Handler {
966    Text(TextHandler),
967    Map(MapHandler),
968    List(ListHandler),
969    MovableList(MovableListHandler),
970    Tree(TreeHandler),
971    #[cfg(feature = "counter")]
972    Counter(counter::CounterHandler),
973    Unknown(UnknownHandler),
974}
975
976impl HandlerTrait for Handler {
977    fn is_attached(&self) -> bool {
978        match self {
979            Self::Text(x) => x.is_attached(),
980            Self::Map(x) => x.is_attached(),
981            Self::List(x) => x.is_attached(),
982            Self::Tree(x) => x.is_attached(),
983            Self::MovableList(x) => x.is_attached(),
984            #[cfg(feature = "counter")]
985            Self::Counter(x) => x.is_attached(),
986            Self::Unknown(x) => x.is_attached(),
987        }
988    }
989
990    fn attached_handler(&self) -> Option<&BasicHandler> {
991        match self {
992            Self::Text(x) => x.attached_handler(),
993            Self::Map(x) => x.attached_handler(),
994            Self::List(x) => x.attached_handler(),
995            Self::MovableList(x) => x.attached_handler(),
996            Self::Tree(x) => x.attached_handler(),
997            #[cfg(feature = "counter")]
998            Self::Counter(x) => x.attached_handler(),
999            Self::Unknown(x) => x.attached_handler(),
1000        }
1001    }
1002
1003    fn get_value(&self) -> LoroValue {
1004        match self {
1005            Self::Text(x) => x.get_value(),
1006            Self::Map(x) => x.get_value(),
1007            Self::List(x) => x.get_value(),
1008            Self::MovableList(x) => x.get_value(),
1009            Self::Tree(x) => x.get_value(),
1010            #[cfg(feature = "counter")]
1011            Self::Counter(x) => x.get_value(),
1012            Self::Unknown(x) => x.get_value(),
1013        }
1014    }
1015
1016    fn get_deep_value(&self) -> LoroValue {
1017        match self {
1018            Self::Text(x) => x.get_deep_value(),
1019            Self::Map(x) => x.get_deep_value(),
1020            Self::List(x) => x.get_deep_value(),
1021            Self::MovableList(x) => x.get_deep_value(),
1022            Self::Tree(x) => x.get_deep_value(),
1023            #[cfg(feature = "counter")]
1024            Self::Counter(x) => x.get_deep_value(),
1025            Self::Unknown(x) => x.get_deep_value(),
1026        }
1027    }
1028
1029    fn kind(&self) -> ContainerType {
1030        match self {
1031            Self::Text(x) => x.kind(),
1032            Self::Map(x) => x.kind(),
1033            Self::List(x) => x.kind(),
1034            Self::MovableList(x) => x.kind(),
1035            Self::Tree(x) => x.kind(),
1036            #[cfg(feature = "counter")]
1037            Self::Counter(x) => x.kind(),
1038            Self::Unknown(x) => x.kind(),
1039        }
1040    }
1041
1042    fn to_handler(&self) -> Handler {
1043        match self {
1044            Self::Text(x) => x.to_handler(),
1045            Self::Map(x) => x.to_handler(),
1046            Self::List(x) => x.to_handler(),
1047            Self::MovableList(x) => x.to_handler(),
1048            Self::Tree(x) => x.to_handler(),
1049            #[cfg(feature = "counter")]
1050            Self::Counter(x) => x.to_handler(),
1051            Self::Unknown(x) => x.to_handler(),
1052        }
1053    }
1054
1055    fn attach(
1056        &self,
1057        txn: &mut Transaction,
1058        parent: &BasicHandler,
1059        self_id: ContainerID,
1060    ) -> LoroResult<Self> {
1061        match self {
1062            Self::Text(x) => Ok(Handler::Text(x.attach(txn, parent, self_id)?)),
1063            Self::Map(x) => Ok(Handler::Map(x.attach(txn, parent, self_id)?)),
1064            Self::List(x) => Ok(Handler::List(x.attach(txn, parent, self_id)?)),
1065            Self::MovableList(x) => Ok(Handler::MovableList(x.attach(txn, parent, self_id)?)),
1066            Self::Tree(x) => Ok(Handler::Tree(x.attach(txn, parent, self_id)?)),
1067            #[cfg(feature = "counter")]
1068            Self::Counter(x) => Ok(Handler::Counter(x.attach(txn, parent, self_id)?)),
1069            Self::Unknown(x) => Ok(Handler::Unknown(x.attach(txn, parent, self_id)?)),
1070        }
1071    }
1072
1073    fn get_attached(&self) -> Option<Self> {
1074        match self {
1075            Self::Text(x) => x.get_attached().map(Handler::Text),
1076            Self::Map(x) => x.get_attached().map(Handler::Map),
1077            Self::List(x) => x.get_attached().map(Handler::List),
1078            Self::MovableList(x) => x.get_attached().map(Handler::MovableList),
1079            Self::Tree(x) => x.get_attached().map(Handler::Tree),
1080            #[cfg(feature = "counter")]
1081            Self::Counter(x) => x.get_attached().map(Handler::Counter),
1082            Self::Unknown(x) => x.get_attached().map(Handler::Unknown),
1083        }
1084    }
1085
1086    fn from_handler(h: Handler) -> Option<Self> {
1087        Some(h)
1088    }
1089
1090    fn doc(&self) -> Option<LoroDoc> {
1091        match self {
1092            Self::Text(x) => x.doc(),
1093            Self::Map(x) => x.doc(),
1094            Self::List(x) => x.doc(),
1095            Self::MovableList(x) => x.doc(),
1096            Self::Tree(x) => x.doc(),
1097            #[cfg(feature = "counter")]
1098            Self::Counter(x) => x.doc(),
1099            Self::Unknown(x) => x.doc(),
1100        }
1101    }
1102}
1103
1104impl Handler {
1105    fn apply_map_container_diff_value(
1106        map: &MapHandler,
1107        key: &str,
1108        old_id: ContainerID,
1109        on_container_remap: &mut dyn FnMut(ContainerID, ContainerID),
1110    ) -> LoroResult<()> {
1111        if old_id.is_mergeable() {
1112            let parent_id = map.id();
1113            let kind = old_id.container_type();
1114            let new_id = ContainerID::new_mergeable(&parent_id, key, kind);
1115            let marker = loro_common::mergeable_marker(&parent_id, key, kind);
1116            map.insert_without_skipping(key, marker)?;
1117            on_container_remap(old_id, new_id);
1118            return Ok(());
1119        }
1120
1121        let new_h = map.insert_container(key, Handler::new_unattached(old_id.container_type()))?;
1122        let new_id = new_h.id();
1123        on_container_remap(old_id, new_id);
1124        Ok(())
1125    }
1126
1127    pub(crate) fn new_attached(id: ContainerID, doc: LoroDoc) -> Self {
1128        let kind = id.container_type();
1129        let handler = BasicHandler {
1130            container_idx: doc.arena.register_container(&id),
1131            id,
1132            doc,
1133        };
1134
1135        match kind {
1136            ContainerType::Map => Self::Map(MapHandler {
1137                inner: handler.into(),
1138            }),
1139            ContainerType::List => Self::List(ListHandler {
1140                inner: handler.into(),
1141            }),
1142            ContainerType::Tree => Self::Tree(TreeHandler {
1143                inner: handler.into(),
1144            }),
1145            ContainerType::Text => Self::Text(TextHandler {
1146                inner: handler.into(),
1147            }),
1148            ContainerType::MovableList => Self::MovableList(MovableListHandler {
1149                inner: handler.into(),
1150            }),
1151            #[cfg(feature = "counter")]
1152            ContainerType::Counter => Self::Counter(counter::CounterHandler {
1153                inner: handler.into(),
1154            }),
1155            ContainerType::Unknown(_) => Self::Unknown(UnknownHandler { inner: handler }),
1156        }
1157    }
1158
1159    #[allow(unused)]
1160    pub(crate) fn new_unattached(kind: ContainerType) -> Self {
1161        match kind {
1162            ContainerType::Text => Self::Text(TextHandler::new_detached()),
1163            ContainerType::Map => Self::Map(MapHandler::new_detached()),
1164            ContainerType::List => Self::List(ListHandler::new_detached()),
1165            ContainerType::Tree => Self::Tree(TreeHandler::new_detached()),
1166            ContainerType::MovableList => Self::MovableList(MovableListHandler::new_detached()),
1167            #[cfg(feature = "counter")]
1168            ContainerType::Counter => Self::Counter(counter::CounterHandler::new_detached()),
1169            ContainerType::Unknown(_) => unreachable!(),
1170        }
1171    }
1172
1173    pub fn id(&self) -> ContainerID {
1174        match self {
1175            Self::Map(x) => x.id(),
1176            Self::List(x) => x.id(),
1177            Self::Text(x) => x.id(),
1178            Self::Tree(x) => x.id(),
1179            Self::MovableList(x) => x.id(),
1180            #[cfg(feature = "counter")]
1181            Self::Counter(x) => x.id(),
1182            Self::Unknown(x) => x.id(),
1183        }
1184    }
1185
1186    pub(crate) fn container_idx(&self) -> ContainerIdx {
1187        match self {
1188            Self::Map(x) => x.idx(),
1189            Self::List(x) => x.idx(),
1190            Self::Text(x) => x.idx(),
1191            Self::Tree(x) => x.idx(),
1192            Self::MovableList(x) => x.idx(),
1193            #[cfg(feature = "counter")]
1194            Self::Counter(x) => x.idx(),
1195            Self::Unknown(x) => x.idx(),
1196        }
1197    }
1198
1199    pub fn c_type(&self) -> ContainerType {
1200        match self {
1201            Self::Map(_) => ContainerType::Map,
1202            Self::List(_) => ContainerType::List,
1203            Self::Text(_) => ContainerType::Text,
1204            Self::Tree(_) => ContainerType::Tree,
1205            Self::MovableList(_) => ContainerType::MovableList,
1206            #[cfg(feature = "counter")]
1207            Self::Counter(_) => ContainerType::Counter,
1208            Self::Unknown(x) => x.id().container_type(),
1209        }
1210    }
1211
1212    fn get_deep_value(&self) -> LoroValue {
1213        match self {
1214            Self::Map(x) => x.get_deep_value(),
1215            Self::List(x) => x.get_deep_value(),
1216            Self::MovableList(x) => x.get_deep_value(),
1217            Self::Text(x) => x.get_deep_value(),
1218            Self::Tree(x) => x.get_deep_value(),
1219            #[cfg(feature = "counter")]
1220            Self::Counter(x) => x.get_deep_value(),
1221            Self::Unknown(x) => x.get_deep_value(),
1222        }
1223    }
1224
1225    pub(crate) fn apply_diff(
1226        &self,
1227        diff: Diff,
1228        container_remap: &mut FxHashMap<ContainerID, ContainerID>,
1229    ) -> LoroResult<()> {
1230        // In this method we will not clone the values of the containers if
1231        // they are remapped. It's the caller's duty to do so
1232        let on_container_remap = &mut |old_id, new_id| {
1233            if old_id != new_id {
1234                container_remap.insert(old_id, new_id);
1235            }
1236        };
1237        match self {
1238            Self::Map(x) => {
1239                let diff = match diff {
1240                    crate::event::Diff::Map(d) => d,
1241                    _ => {
1242                        return Err(LoroError::DecodeError(
1243                            "Invalid diff type for map container".into(),
1244                        ));
1245                    }
1246                };
1247                for (key, value) in diff.updated.into_iter() {
1248                    match value.value {
1249                        Some(ValueOrHandler::Handler(h)) => {
1250                            Self::apply_map_container_diff_value(
1251                                x,
1252                                &key,
1253                                h.id(),
1254                                on_container_remap,
1255                            )?;
1256                        }
1257                        Some(ValueOrHandler::Value(LoroValue::Container(old_id))) => {
1258                            Self::apply_map_container_diff_value(
1259                                x,
1260                                &key,
1261                                old_id,
1262                                on_container_remap,
1263                            )?;
1264                        }
1265                        Some(ValueOrHandler::Value(v)) => {
1266                            x.insert_without_skipping(&key, v)?;
1267                        }
1268                        None => {
1269                            x.delete(&key)?;
1270                        }
1271                    }
1272                }
1273            }
1274            Self::Text(x) => {
1275                let delta = match diff {
1276                    crate::event::Diff::Text(d) => d,
1277                    _ => {
1278                        return Err(LoroError::DecodeError(
1279                            "Invalid diff type for text container".into(),
1280                        ));
1281                    }
1282                };
1283                x.apply_delta(&TextDelta::from_text_diff(delta.iter()))?;
1284            }
1285            Self::List(x) => {
1286                let delta = match diff {
1287                    crate::event::Diff::List(d) => d,
1288                    _ => {
1289                        return Err(LoroError::DecodeError(
1290                            "Invalid diff type for list container".into(),
1291                        ));
1292                    }
1293                };
1294                x.apply_delta(delta, on_container_remap)?;
1295            }
1296            Self::MovableList(x) => {
1297                let delta = match diff {
1298                    crate::event::Diff::List(d) => d,
1299                    _ => {
1300                        return Err(LoroError::DecodeError(
1301                            "Invalid diff type for movable list container".into(),
1302                        ));
1303                    }
1304                };
1305                x.apply_delta(delta, container_remap)?;
1306            }
1307            Self::Tree(x) => {
1308                fn remap_tree_id(
1309                    id: &mut TreeID,
1310                    container_remap: &FxHashMap<ContainerID, ContainerID>,
1311                ) {
1312                    let mut remapped = false;
1313                    let mut map_id = id.associated_meta_container();
1314                    while let Some(rid) = container_remap.get(&map_id) {
1315                        remapped = true;
1316                        map_id = rid.clone();
1317                    }
1318                    if remapped {
1319                        *id = TreeID::new(
1320                            *map_id.as_normal().unwrap().0,
1321                            *map_id.as_normal().unwrap().1,
1322                        )
1323                    }
1324                }
1325                let tree_diff = match diff {
1326                    crate::event::Diff::Tree(d) => d,
1327                    _ => {
1328                        return Err(LoroError::DecodeError(
1329                            "Invalid diff type for tree container".into(),
1330                        ));
1331                    }
1332                };
1333                for diff in tree_diff.diff {
1334                    let mut target = diff.target;
1335                    match diff.action {
1336                        TreeExternalDiff::Create {
1337                            mut parent,
1338                            index: _,
1339                            position,
1340                        } => {
1341                            if let TreeParentId::Node(p) = &mut parent {
1342                                remap_tree_id(p, container_remap)
1343                            }
1344                            remap_tree_id(&mut target, container_remap);
1345                            if !x.is_node_unexist(&target) && !x.is_node_deleted(&target)? {
1346                                // 1@0 is the parent of 2@1
1347                                // ┌────┐    ┌───────────────┐
1348                                // │xxxx│◀───│Move 2@1 to 0@0◀┐
1349                                // └────┘    └───────────────┘│
1350                                // ┌───────┐                  │ ┌────────┐
1351                                // │Del 1@0│◀─────────────────┴─│Meta 2@1│ ◀───  undo 2 ops redo 2 ops
1352                                // └───────┘                    └────────┘
1353                                //
1354                                // When we undo the delete operation, we should not create a new tree node and its child.
1355                                // However, the concurrent operation has moved the child to another parent. It's still alive.
1356                                // So when we redo the delete operation, we should check if the target is still alive.
1357                                // If it's alive, we should move it back instead of creating new one.
1358                                x.move_at_with_target_for_apply_diff(parent, position, target)?;
1359                            } else {
1360                                let new_target = x.__internal__next_tree_id();
1361                                if x.create_at_with_target_for_apply_diff(
1362                                    parent, position, new_target,
1363                                )? {
1364                                    container_remap.insert(
1365                                        target.associated_meta_container(),
1366                                        new_target.associated_meta_container(),
1367                                    );
1368                                }
1369                            }
1370                        }
1371                        TreeExternalDiff::Move {
1372                            mut parent,
1373                            index: _,
1374                            position,
1375                            old_parent: _,
1376                            old_index: _,
1377                        } => {
1378                            if let TreeParentId::Node(p) = &mut parent {
1379                                remap_tree_id(p, container_remap)
1380                            }
1381                            remap_tree_id(&mut target, container_remap);
1382                            // determine if the target is deleted
1383                            if x.is_node_unexist(&target) || x.is_node_deleted(&target)? {
1384                                // create the target node, we should use the new target id
1385                                let new_target = x.__internal__next_tree_id();
1386                                if x.create_at_with_target_for_apply_diff(
1387                                    parent, position, new_target,
1388                                )? {
1389                                    container_remap.insert(
1390                                        target.associated_meta_container(),
1391                                        new_target.associated_meta_container(),
1392                                    );
1393                                }
1394                            } else {
1395                                x.move_at_with_target_for_apply_diff(parent, position, target)?;
1396                            }
1397                        }
1398                        TreeExternalDiff::Delete { .. } => {
1399                            remap_tree_id(&mut target, container_remap);
1400                            if !x.is_node_deleted(&target)? {
1401                                x.delete(target)?;
1402                            }
1403                        }
1404                    }
1405                }
1406            }
1407            #[cfg(feature = "counter")]
1408            Self::Counter(x) => {
1409                let delta = match diff {
1410                    crate::event::Diff::Counter(d) => d,
1411                    _ => {
1412                        return Err(LoroError::DecodeError(
1413                            "Invalid diff type for counter container".into(),
1414                        ));
1415                    }
1416                };
1417                x.increment(delta)?;
1418            }
1419            Self::Unknown(_) => {
1420                // do nothing
1421            }
1422        }
1423
1424        Ok(())
1425    }
1426
1427    pub fn clear(&self) -> LoroResult<()> {
1428        match self {
1429            Handler::Text(text_handler) => text_handler.clear(),
1430            Handler::Map(map_handler) => map_handler.clear(),
1431            Handler::List(list_handler) => list_handler.clear(),
1432            Handler::MovableList(movable_list_handler) => movable_list_handler.clear(),
1433            Handler::Tree(tree_handler) => tree_handler.clear(),
1434            #[cfg(feature = "counter")]
1435            Handler::Counter(counter_handler) => counter_handler.clear(),
1436            Handler::Unknown(_unknown_handler) => Ok(()),
1437        }
1438    }
1439}
1440
1441#[derive(Clone, EnumAsInner, Debug)]
1442pub enum ValueOrHandler {
1443    Value(LoroValue),
1444    Handler(Handler),
1445}
1446
1447impl ValueOrHandler {
1448    pub(crate) fn from_value(value: LoroValue, doc: &Arc<LoroDocInner>) -> Self {
1449        if let LoroValue::Container(c) = value {
1450            ValueOrHandler::Handler(Handler::new_attached(c, LoroDoc::from_inner(doc.clone())))
1451        } else {
1452            ValueOrHandler::Value(value)
1453        }
1454    }
1455
1456    pub(crate) fn to_value(&self) -> LoroValue {
1457        match self {
1458            Self::Value(v) => v.clone(),
1459            Self::Handler(h) => LoroValue::Container(h.id().clone()),
1460        }
1461    }
1462
1463    pub(crate) fn to_deep_value(&self) -> LoroValue {
1464        match self {
1465            Self::Value(v) => v.clone(),
1466            Self::Handler(h) => h.get_deep_value(),
1467        }
1468    }
1469}
1470
1471impl From<LoroValue> for ValueOrHandler {
1472    fn from(value: LoroValue) -> Self {
1473        ValueOrHandler::Value(value)
1474    }
1475}
1476
1477impl TextHandler {
1478    /// Create a new container that is detached from the document.
1479    ///
1480    /// The edits on a detached container will not be persisted.
1481    /// To attach the container to the document, please insert it into an attached container.
1482    pub fn new_detached() -> Self {
1483        Self {
1484            inner: MaybeDetached::new_detached(RichtextState::default()),
1485        }
1486    }
1487
1488    /// Get the version id of the richtext
1489    ///
1490    /// This can be used to detect whether the richtext is changed
1491    pub fn version_id(&self) -> Option<usize> {
1492        match &self.inner {
1493            MaybeDetached::Detached(_) => None,
1494            MaybeDetached::Attached(a) => {
1495                Some(a.with_state(|state| state.as_richtext_state_mut().unwrap().get_version_id()))
1496            }
1497        }
1498    }
1499
1500    pub fn get_richtext_value(&self) -> LoroValue {
1501        match &self.inner {
1502            MaybeDetached::Detached(t) => {
1503                let t = t.lock();
1504                t.value.get_richtext_value()
1505            }
1506            MaybeDetached::Attached(a) => {
1507                a.with_state(|state| state.as_richtext_state_mut().unwrap().get_richtext_value())
1508            }
1509        }
1510    }
1511
1512    pub fn is_empty(&self) -> bool {
1513        match &self.inner {
1514            MaybeDetached::Detached(t) => t.lock().value.is_empty(),
1515            MaybeDetached::Attached(a) if a.has_decoded_state() => {
1516                a.with_state(|state| state.as_richtext_state_mut().unwrap().is_empty())
1517            }
1518            MaybeDetached::Attached(a) => a.get_value().as_string().unwrap().is_empty(),
1519        }
1520    }
1521
1522    pub fn len_utf8(&self) -> usize {
1523        match &self.inner {
1524            MaybeDetached::Detached(t) => {
1525                let t = t.lock();
1526                t.value.len_utf8()
1527            }
1528            MaybeDetached::Attached(a) => {
1529                a.with_doc_state(|state| state.get_text_len(a.container_idx, PosType::Bytes))
1530            }
1531        }
1532    }
1533
1534    pub fn len_utf16(&self) -> usize {
1535        match &self.inner {
1536            MaybeDetached::Detached(t) => {
1537                let t = t.lock();
1538                t.value.len_utf16()
1539            }
1540            MaybeDetached::Attached(a) => {
1541                a.with_doc_state(|state| state.get_text_len(a.container_idx, PosType::Utf16))
1542            }
1543        }
1544    }
1545
1546    pub fn len_unicode(&self) -> usize {
1547        match &self.inner {
1548            MaybeDetached::Detached(t) => {
1549                let t = t.lock();
1550                t.value.len_unicode()
1551            }
1552            MaybeDetached::Attached(a) => {
1553                a.with_doc_state(|state| state.get_text_len(a.container_idx, PosType::Unicode))
1554            }
1555        }
1556    }
1557
1558    /// if `wasm` feature is enabled, it is a UTF-16 length
1559    /// otherwise, it is a Unicode length
1560    pub fn len_event(&self) -> usize {
1561        if cfg!(feature = "wasm") {
1562            self.len_utf16()
1563        } else {
1564            self.len_unicode()
1565        }
1566    }
1567
1568    fn len(&self, pos_type: PosType) -> usize {
1569        match &self.inner {
1570            MaybeDetached::Detached(t) => t.lock().value.len(pos_type),
1571            MaybeDetached::Attached(a) => {
1572                a.with_doc_state(|state| state.get_text_len(a.container_idx, pos_type))
1573            }
1574        }
1575    }
1576
1577    fn validate_text_boundary(&self, pos: usize, pos_type: PosType) -> LoroResult<()> {
1578        let err = match pos_type {
1579            PosType::Bytes => Some(LoroError::UTF8InUnicodeCodePoint { pos }),
1580            PosType::Utf16 => Some(LoroError::UTF16InUnicodeCodePoint { pos }),
1581            PosType::Event if cfg!(feature = "wasm") => {
1582                Some(LoroError::UTF16InUnicodeCodePoint { pos })
1583            }
1584            _ => None,
1585        };
1586
1587        let Some(err) = err else {
1588            return Ok(());
1589        };
1590
1591        let len = self.len(pos_type);
1592        if pos > len {
1593            return Ok(());
1594        }
1595
1596        if self.all_text_positions_are_boundaries(pos_type, len) {
1597            return Ok(());
1598        }
1599
1600        let Some(unicode_pos) = self.convert_pos(pos, pos_type, PosType::Unicode) else {
1601            return Err(err);
1602        };
1603        if self.convert_pos(unicode_pos, PosType::Unicode, pos_type) != Some(pos) {
1604            return Err(err);
1605        }
1606
1607        Ok(())
1608    }
1609
1610    fn all_text_positions_are_boundaries(&self, pos_type: PosType, len: usize) -> bool {
1611        match pos_type {
1612            PosType::Bytes => len == self.len_unicode(),
1613            PosType::Utf16 => len == self.len_unicode(),
1614            PosType::Event if cfg!(feature = "wasm") => len == self.len_unicode(),
1615            _ => false,
1616        }
1617    }
1618
1619    pub fn diagnose(&self) {
1620        match &self.inner {
1621            MaybeDetached::Detached(t) => {
1622                let t = t.lock();
1623                t.value.diagnose();
1624            }
1625            MaybeDetached::Attached(a) => {
1626                a.with_state(|state| state.as_richtext_state_mut().unwrap().diagnose());
1627            }
1628        }
1629    }
1630
1631    pub fn iter(&self, mut callback: impl FnMut(&str) -> bool) {
1632        // Do not call user callbacks while holding the state lock; callbacks may re-enter Loro.
1633        let spans: Vec<String> = match &self.inner {
1634            MaybeDetached::Detached(t) => {
1635                let t = t.lock();
1636                t.value
1637                    .iter()
1638                    .map(|span| span.text.as_str().to_owned())
1639                    .collect()
1640            }
1641            MaybeDetached::Attached(a) => a.with_state(|state| {
1642                let mut spans = Vec::new();
1643                state.as_richtext_state_mut().unwrap().iter(|span| {
1644                    spans.push(span.to_owned());
1645                    true
1646                });
1647                spans
1648            }),
1649        };
1650
1651        for span in spans {
1652            if !callback(span.as_str()) {
1653                return;
1654            }
1655        }
1656    }
1657
1658    /// Get a character at `pos` in the coordinate system specified by `pos_type`.
1659    pub fn char_at(&self, pos: usize, pos_type: PosType) -> LoroResult<char> {
1660        let len = self.len(pos_type);
1661        if pos >= len {
1662            return Err(LoroError::OutOfBound {
1663                pos,
1664                len,
1665                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1666            });
1667        }
1668        if let Ok(c) = match &self.inner {
1669            MaybeDetached::Detached(t) => {
1670                let t = t.lock();
1671                let event_pos = match pos_type {
1672                    PosType::Event => pos,
1673                    _ => t.value.index_to_event_index(pos, pos_type),
1674                };
1675                t.value.get_char_by_event_index(event_pos)
1676            }
1677            MaybeDetached::Attached(a) if a.has_decoded_state() || pos_type == PosType::Entity => a
1678                .with_state(|state| {
1679                    let state = state.as_richtext_state_mut().unwrap();
1680                    let event_pos = match pos_type {
1681                        PosType::Event => pos,
1682                        _ => state.index_to_event_index(pos, pos_type),
1683                    };
1684                    state.get_char_by_event_index(event_pos)
1685                }),
1686            MaybeDetached::Attached(a) => {
1687                return text_char_at(a.get_value().as_string().unwrap(), pos, pos_type);
1688            }
1689        } {
1690            Ok(c)
1691        } else {
1692            Err(LoroError::OutOfBound {
1693                pos,
1694                len,
1695                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1696            })
1697        }
1698    }
1699
1700    /// `start_index` and `end_index` are Event Index:
1701    ///
1702    /// - if feature="wasm", pos is a UTF-16 index
1703    /// - if feature!="wasm", pos is a Unicode index
1704    ///
1705    pub fn slice(
1706        &self,
1707        start_index: usize,
1708        end_index: usize,
1709        pos_type: PosType,
1710    ) -> LoroResult<String> {
1711        self.slice_with_pos_type(start_index, end_index, pos_type)
1712    }
1713
1714    pub fn slice_utf16(&self, start_index: usize, end_index: usize) -> LoroResult<String> {
1715        self.slice(start_index, end_index, PosType::Utf16)
1716    }
1717
1718    fn slice_with_pos_type(
1719        &self,
1720        start_index: usize,
1721        end_index: usize,
1722        pos_type: PosType,
1723    ) -> LoroResult<String> {
1724        if end_index < start_index {
1725            return Err(LoroError::EndIndexLessThanStartIndex {
1726                start: start_index,
1727                end: end_index,
1728            });
1729        }
1730        if start_index == end_index {
1731            return Ok(String::new());
1732        }
1733
1734        let info = || format!("Position: {}:{}", file!(), line!()).into_boxed_str();
1735        match &self.inner {
1736            MaybeDetached::Detached(t) => {
1737                let t = t.lock();
1738                let len = t.value.len(pos_type);
1739                if end_index > len {
1740                    return Err(LoroError::OutOfBound {
1741                        pos: end_index,
1742                        len,
1743                        info: info(),
1744                    });
1745                }
1746                let (start, end) = match pos_type {
1747                    PosType::Event => (start_index, end_index),
1748                    _ => (
1749                        t.value.index_to_event_index(start_index, pos_type),
1750                        t.value.index_to_event_index(end_index, pos_type),
1751                    ),
1752                };
1753                t.value.get_text_slice_by_event_index(start, end - start)
1754            }
1755            MaybeDetached::Attached(a) if a.has_decoded_state() || pos_type == PosType::Entity => a
1756                .with_state(|state| {
1757                    let state = state.as_richtext_state_mut().unwrap();
1758                    let len = state.len(pos_type);
1759                    if end_index > len {
1760                        return Err(LoroError::OutOfBound {
1761                            pos: end_index,
1762                            len,
1763                            info: info(),
1764                        });
1765                    }
1766                    let (start, end) = match pos_type {
1767                        PosType::Event => (start_index, end_index),
1768                        _ => (
1769                            state.index_to_event_index(start_index, pos_type),
1770                            state.index_to_event_index(end_index, pos_type),
1771                        ),
1772                    };
1773                    state.get_text_slice_by_event_index(start, end - start)
1774                }),
1775            MaybeDetached::Attached(a) => text_slice(
1776                a.get_value().as_string().unwrap(),
1777                start_index,
1778                end_index,
1779                pos_type,
1780            )
1781            .map_err(|err| match err {
1782                LoroError::OutOfBound { pos, len, .. } => LoroError::OutOfBound {
1783                    pos,
1784                    len,
1785                    info: info(),
1786                },
1787                err => err,
1788            }),
1789        }
1790    }
1791
1792    pub fn slice_delta(
1793        &self,
1794        start_index: usize,
1795        end_index: usize,
1796        pos_type: PosType,
1797    ) -> LoroResult<Vec<TextDelta>> {
1798        if end_index < start_index {
1799            return Err(LoroError::EndIndexLessThanStartIndex {
1800                start: start_index,
1801                end: end_index,
1802            });
1803        }
1804        if start_index == end_index {
1805            return Ok(Vec::new());
1806        }
1807
1808        let len = self.len(pos_type);
1809        if end_index > len {
1810            return Err(LoroError::OutOfBound {
1811                pos: end_index,
1812                len,
1813                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1814            });
1815        }
1816        self.validate_text_boundary(start_index, pos_type)?;
1817        self.validate_text_boundary(end_index, pos_type)?;
1818
1819        match &self.inner {
1820            MaybeDetached::Detached(t) => {
1821                let t = t.lock();
1822                let ans = t.value.slice_delta(start_index, end_index, pos_type)?;
1823                Ok(ans
1824                    .into_iter()
1825                    .map(|(s, a)| TextDelta::Insert {
1826                        insert: s,
1827                        attributes: a.to_option_map_without_null_value(),
1828                    })
1829                    .collect())
1830            }
1831            MaybeDetached::Attached(a) => a.with_state(|state| {
1832                let ans = state.as_richtext_state_mut().unwrap().slice_delta(
1833                    start_index,
1834                    end_index,
1835                    pos_type,
1836                )?;
1837                Ok(ans
1838                    .into_iter()
1839                    .map(|(s, a)| TextDelta::Insert {
1840                        insert: s,
1841                        attributes: a.to_option_map_without_null_value(),
1842                    })
1843                    .collect())
1844            }),
1845        }
1846    }
1847
1848    /// `pos` is a Event Index:
1849    ///
1850    /// - if feature="wasm", pos is a UTF-16 index
1851    /// - if feature!="wasm", pos is a Unicode index
1852    ///
1853    /// This method requires auto_commit to be enabled.
1854    pub fn splice(&self, pos: usize, len: usize, s: &str, pos_type: PosType) -> LoroResult<String> {
1855        let end = checked_range_end(
1856            pos,
1857            len,
1858            self.len(pos_type),
1859            format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1860        )?;
1861        let x = self.slice(pos, end, pos_type)?;
1862        self.delete(pos, len, pos_type)?;
1863        self.insert(pos, s, pos_type)?;
1864        Ok(x)
1865    }
1866
1867    pub fn splice_utf8(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1868        // let x = self.slice(pos, pos + len)?;
1869        self.delete_utf8(pos, len)?;
1870        self.insert_utf8(pos, s)?;
1871        Ok(())
1872    }
1873
1874    pub fn splice_utf16(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1875        self.delete(pos, len, PosType::Utf16)?;
1876        self.insert(pos, s, PosType::Utf16)?;
1877        Ok(())
1878    }
1879
1880    /// Insert text at `pos` using the given `pos_type` coordinate system.
1881    ///
1882    /// This method requires auto_commit to be enabled.
1883    pub fn insert(&self, pos: usize, s: &str, pos_type: PosType) -> LoroResult<()> {
1884        match &self.inner {
1885            MaybeDetached::Detached(t) => {
1886                let len = self.len(pos_type);
1887                if pos > len {
1888                    return Err(LoroError::OutOfBound {
1889                        pos,
1890                        len,
1891                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1892                    });
1893                }
1894                self.validate_text_boundary(pos, pos_type)?;
1895
1896                let mut t = t.lock();
1897                let (index, _) = t
1898                    .value
1899                    .get_entity_index_for_text_insert(pos, pos_type)
1900                    .unwrap();
1901                t.value.insert_at_entity_index(
1902                    index,
1903                    BytesSlice::from_bytes(s.as_bytes()),
1904                    IdFull::NONE_ID,
1905                );
1906                Ok(())
1907            }
1908            MaybeDetached::Attached(a) => {
1909                if s.is_empty() {
1910                    let len = self.len(pos_type);
1911                    if pos > len {
1912                        return Err(LoroError::OutOfBound {
1913                            pos,
1914                            len,
1915                            info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1916                        });
1917                    }
1918                    self.validate_text_boundary(pos, pos_type)?;
1919                    return Ok(());
1920                }
1921
1922                a.with_txn(|txn| self.insert_with_txn(txn, pos, s, pos_type))
1923            }
1924        }
1925    }
1926
1927    pub fn insert_utf8(&self, pos: usize, s: &str) -> LoroResult<()> {
1928        self.insert(pos, s, PosType::Bytes)
1929    }
1930
1931    pub fn insert_utf16(&self, pos: usize, s: &str) -> LoroResult<()> {
1932        self.insert(pos, s, PosType::Utf16)
1933    }
1934
1935    pub fn insert_unicode(&self, pos: usize, s: &str) -> LoroResult<()> {
1936        self.insert(pos, s, PosType::Unicode)
1937    }
1938
1939    /// Insert text within an existing transaction using the provided `pos_type`.
1940    pub fn insert_with_txn(
1941        &self,
1942        txn: &mut Transaction,
1943        pos: usize,
1944        s: &str,
1945        pos_type: PosType,
1946    ) -> LoroResult<()> {
1947        self.insert_with_txn_and_attr(txn, pos, s, None, pos_type)?;
1948        Ok(())
1949    }
1950
1951    pub fn insert_with_txn_utf8(
1952        &self,
1953        txn: &mut Transaction,
1954        pos: usize,
1955        s: &str,
1956    ) -> LoroResult<()> {
1957        self.insert_with_txn(txn, pos, s, PosType::Bytes)
1958    }
1959
1960    /// Delete a span using the coordinate system described by `pos_type`.
1961    ///
1962    /// This method requires auto_commit to be enabled.
1963    pub fn delete(&self, pos: usize, len: usize, pos_type: PosType) -> LoroResult<()> {
1964        if len == 0 {
1965            return Ok(());
1966        }
1967
1968        let text_len = self.len(pos_type);
1969        let end = checked_range_end(
1970            pos,
1971            len,
1972            text_len,
1973            format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1974        )?;
1975        self.validate_text_boundary(pos, pos_type)?;
1976        self.validate_text_boundary(end, pos_type)?;
1977
1978        match &self.inner {
1979            MaybeDetached::Detached(t) => {
1980                let mut t = t.lock();
1981                let ranges = t.value.get_text_entity_ranges(pos, len, pos_type)?;
1982                for range in ranges.iter().rev() {
1983                    t.value
1984                        .drain_by_entity_index(range.entity_start, range.entity_len(), None);
1985                }
1986                Ok(())
1987            }
1988            MaybeDetached::Attached(a) => {
1989                a.with_txn(|txn| self.delete_with_txn(txn, pos, len, pos_type))
1990            }
1991        }
1992    }
1993
1994    pub fn delete_utf8(&self, pos: usize, len: usize) -> LoroResult<()> {
1995        self.delete(pos, len, PosType::Bytes)
1996    }
1997
1998    pub fn delete_utf16(&self, pos: usize, len: usize) -> LoroResult<()> {
1999        self.delete(pos, len, PosType::Utf16)
2000    }
2001
2002    pub fn delete_unicode(&self, pos: usize, len: usize) -> LoroResult<()> {
2003        self.delete(pos, len, PosType::Unicode)
2004    }
2005
2006    /// If attr is specified, it will be used as the attribute of the inserted text.
2007    /// It will override the existing attribute of the text.
2008    fn insert_with_txn_and_attr(
2009        &self,
2010        txn: &mut Transaction,
2011        pos: usize,
2012        s: &str,
2013        attr: Option<&FxHashMap<String, LoroValue>>,
2014        pos_type: PosType,
2015    ) -> Result<Vec<(InternalString, LoroValue)>, LoroError> {
2016        if s.is_empty() {
2017            return Ok(Vec::new());
2018        }
2019
2020        match pos_type {
2021            PosType::Event => {
2022                if pos > self.len_event() {
2023                    return Err(LoroError::OutOfBound {
2024                        pos,
2025                        len: self.len_event(),
2026                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2027                    });
2028                }
2029            }
2030            PosType::Bytes => {
2031                if pos > self.len_utf8() {
2032                    return Err(LoroError::OutOfBound {
2033                        pos,
2034                        len: self.len_utf8(),
2035                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2036                    });
2037                }
2038            }
2039            PosType::Unicode => {
2040                if pos > self.len_unicode() {
2041                    return Err(LoroError::OutOfBound {
2042                        pos,
2043                        len: self.len_unicode(),
2044                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2045                    });
2046                }
2047            }
2048            PosType::Entity => {}
2049            PosType::Utf16 => {
2050                if pos > self.len_utf16() {
2051                    return Err(LoroError::OutOfBound {
2052                        pos,
2053                        len: self.len_utf16(),
2054                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2055                    });
2056                }
2057            }
2058        }
2059        self.validate_text_boundary(pos, pos_type)?;
2060
2061        let inner = self.inner.try_attached_state()?;
2062        let (entity_index, event_index, styles) = inner.with_state(|state| {
2063            let richtext_state = state.as_richtext_state_mut().unwrap();
2064            let ret = richtext_state.get_entity_index_for_text_insert(pos, pos_type);
2065            let (entity_index, cursor) = match ret {
2066                Err(_) => match pos_type {
2067                    PosType::Bytes => {
2068                        return (
2069                            Err(LoroError::UTF8InUnicodeCodePoint { pos }),
2070                            0,
2071                            StyleMeta::empty(),
2072                        );
2073                    }
2074                    PosType::Utf16 | PosType::Event => {
2075                        return (
2076                            Err(LoroError::UTF16InUnicodeCodePoint { pos }),
2077                            0,
2078                            StyleMeta::empty(),
2079                        );
2080                    }
2081                    _ => unreachable!(),
2082                },
2083                Ok(x) => x,
2084            };
2085            let event_index = if let Some(cursor) = cursor {
2086                if pos_type == PosType::Event {
2087                    debug_assert_eq!(
2088                        richtext_state.get_event_index_by_cursor(cursor),
2089                        pos,
2090                        "pos={} cursor={:?} state={:#?}",
2091                        pos,
2092                        cursor,
2093                        &richtext_state
2094                    );
2095                    pos
2096                } else {
2097                    richtext_state.get_event_index_by_cursor(cursor)
2098                }
2099            } else {
2100                assert_eq!(entity_index, 0);
2101                0
2102            };
2103            let styles = richtext_state.get_styles_at_entity_index(entity_index);
2104            (Ok(entity_index), event_index, styles)
2105        });
2106
2107        let entity_index = match entity_index {
2108            Err(x) => return Err(x),
2109            _ => entity_index.unwrap(),
2110        };
2111
2112        let mut override_styles = Vec::new();
2113        if let Some(attr) = attr {
2114            // current styles
2115            let map: FxHashMap<_, _> = styles.iter().map(|x| (x.0.clone(), x.1.data)).collect();
2116            for (key, style) in map.iter() {
2117                match attr.get(key.deref()) {
2118                    Some(v) if v == style => {}
2119                    new_style_value => {
2120                        // need to override
2121                        let new_style_value = new_style_value.cloned().unwrap_or(LoroValue::Null);
2122                        override_styles.push((key.clone(), new_style_value));
2123                    }
2124                }
2125            }
2126
2127            for (key, style) in attr.iter() {
2128                let key = key.as_str().into();
2129                if !map.contains_key(&key) {
2130                    override_styles.push((key, style.clone()));
2131                }
2132            }
2133        }
2134
2135        let unicode_len = s.chars().count();
2136        let event_len = if cfg!(feature = "wasm") {
2137            count_utf16_len(s.as_bytes())
2138        } else {
2139            unicode_len
2140        };
2141
2142        txn.apply_local_op(
2143            inner.container_idx,
2144            crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
2145                slice: ListSlice::RawStr {
2146                    str: Cow::Borrowed(s),
2147                    unicode_len,
2148                },
2149                pos: entity_index,
2150            }),
2151            EventHint::InsertText {
2152                pos: event_index as u32,
2153                styles,
2154                unicode_len: unicode_len as u32,
2155                event_len: event_len as u32,
2156            },
2157            &inner.doc,
2158        )?;
2159
2160        Ok(override_styles)
2161    }
2162
2163    /// Delete text within a transaction using the specified `pos_type`.
2164    pub fn delete_with_txn(
2165        &self,
2166        txn: &mut Transaction,
2167        pos: usize,
2168        len: usize,
2169        pos_type: PosType,
2170    ) -> LoroResult<()> {
2171        self.delete_with_txn_inline(txn, pos, len, pos_type)
2172    }
2173
2174    fn delete_with_txn_inline(
2175        &self,
2176        txn: &mut Transaction,
2177        pos: usize,
2178        len: usize,
2179        pos_type: PosType,
2180    ) -> LoroResult<()> {
2181        if len == 0 {
2182            return Ok(());
2183        }
2184
2185        let text_len = self.len(pos_type);
2186        let end = checked_range_end(
2187            pos,
2188            len,
2189            text_len,
2190            format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2191        )
2192        .inspect_err(|_| error!("pos={} len={} len={}", pos, len, text_len))?;
2193        self.validate_text_boundary(pos, pos_type)?;
2194        self.validate_text_boundary(end, pos_type)?;
2195
2196        let inner = self.inner.try_attached_state()?;
2197        let s = tracing::span!(tracing::Level::INFO, "delete", "pos={} len={}", pos, len);
2198        let _e = s.enter();
2199        let mut event_pos = 0;
2200        let mut event_len = 0;
2201        let ranges = inner.with_state(|state| {
2202            let richtext_state = state.as_richtext_state_mut().unwrap();
2203            event_pos = richtext_state.index_to_event_index(pos, pos_type);
2204            let event_end = richtext_state.index_to_event_index(end, pos_type);
2205            event_len = event_end - event_pos;
2206
2207            richtext_state.get_text_entity_ranges_in_event_index_range(event_pos, event_len)
2208        })?;
2209
2210        //debug_assert_eq!(ranges.iter().map(|x| x.event_len).sum::<usize>(), len);
2211        let pos = event_pos as isize;
2212        let len = event_len as isize;
2213        let mut event_end = pos + len;
2214        for range in ranges.iter().rev() {
2215            let event_start = event_end - range.event_len as isize;
2216            txn.apply_local_op(
2217                inner.container_idx,
2218                crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
2219                    range.id_start,
2220                    range.entity_start as isize,
2221                    range.entity_len() as isize,
2222                ))),
2223                EventHint::DeleteText {
2224                    span: DeleteSpan {
2225                        pos: event_start,
2226                        signed_len: range.event_len as isize,
2227                    },
2228                    unicode_len: range.entity_len(),
2229                },
2230                &inner.doc,
2231            )?;
2232            event_end = event_start;
2233        }
2234
2235        Ok(())
2236    }
2237
2238    /// `start` and `end` are interpreted using `pos_type`.
2239    ///
2240    /// This method requires auto_commit to be enabled.
2241    pub fn mark(
2242        &self,
2243        start: usize,
2244        end: usize,
2245        key: impl Into<InternalString>,
2246        value: LoroValue,
2247        pos_type: PosType,
2248    ) -> LoroResult<()> {
2249        match &self.inner {
2250            MaybeDetached::Detached(t) => {
2251                let mut g = t.lock();
2252                self.mark_for_detached(&mut g.value, key, &value, start, end, pos_type)
2253            }
2254            MaybeDetached::Attached(a) => {
2255                a.with_txn(|txn| self.mark_with_txn(txn, start, end, key, value, pos_type))
2256            }
2257        }
2258    }
2259
2260    fn mark_for_detached(
2261        &self,
2262        state: &mut RichtextState,
2263        key: impl Into<InternalString>,
2264        value: &LoroValue,
2265        start: usize,
2266        end: usize,
2267        pos_type: PosType,
2268    ) -> Result<(), LoroError> {
2269        let key: InternalString = key.into();
2270        let is_delete = matches!(value, &LoroValue::Null);
2271        if start >= end {
2272            return Err(loro_common::LoroError::ArgErr(
2273                "Start must be less than end".to_string().into_boxed_str(),
2274            ));
2275        }
2276        ensure_no_regular_container_value(value)?;
2277
2278        let len = state.len(pos_type);
2279        if end > len {
2280            return Err(LoroError::OutOfBound {
2281                pos: end,
2282                len,
2283                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2284            });
2285        }
2286        let (entity_range, styles) =
2287            state.get_entity_range_and_text_styles_at_range(start..end, pos_type);
2288        if let Some(styles) = styles {
2289            if styles.has_key_value(&key, value) {
2290                return Ok(());
2291            }
2292        }
2293
2294        let has_target_style =
2295            state.range_has_style_key(entity_range.clone(), &StyleKey::Key(key.clone()));
2296        let missing_style_key = is_delete && !has_target_style;
2297
2298        if missing_style_key {
2299            return Ok(());
2300        }
2301
2302        let style_op = Arc::new(StyleOp {
2303            lamport: 0,
2304            peer: 0,
2305            cnt: 0,
2306            key,
2307            value: value.clone(),
2308            // TODO: describe this behavior in the document
2309            info: if is_delete {
2310                TextStyleInfoFlag::BOLD.to_delete()
2311            } else {
2312                TextStyleInfoFlag::BOLD
2313            },
2314        });
2315        state.mark_with_entity_index(entity_range, style_op);
2316        Ok(())
2317    }
2318
2319    /// `start` and `end` are interpreted using `pos_type`.
2320    pub fn unmark(
2321        &self,
2322        start: usize,
2323        end: usize,
2324        key: impl Into<InternalString>,
2325        pos_type: PosType,
2326    ) -> LoroResult<()> {
2327        match &self.inner {
2328            MaybeDetached::Detached(t) => self.mark_for_detached(
2329                &mut t.lock().value,
2330                key,
2331                &LoroValue::Null,
2332                start,
2333                end,
2334                pos_type,
2335            ),
2336            MaybeDetached::Attached(a) => a.with_txn(|txn| {
2337                self.mark_with_txn(txn, start, end, key, LoroValue::Null, pos_type)
2338            }),
2339        }
2340    }
2341
2342    /// `start` and `end` are interpreted using `pos_type`.
2343    pub fn mark_with_txn(
2344        &self,
2345        txn: &mut Transaction,
2346        start: usize,
2347        end: usize,
2348        key: impl Into<InternalString>,
2349        value: LoroValue,
2350        pos_type: PosType,
2351    ) -> LoroResult<()> {
2352        if start >= end {
2353            return Err(loro_common::LoroError::ArgErr(
2354                "Start must be less than end".to_string().into_boxed_str(),
2355            ));
2356        }
2357        ensure_no_regular_container_value(&value)?;
2358
2359        let inner = self.inner.try_attached_state()?;
2360        let key: InternalString = key.into();
2361        let is_delete = matches!(&value, &LoroValue::Null);
2362
2363        let mut doc_state = inner.doc.state.lock();
2364        let len = doc_state.with_state_mut(inner.container_idx, |state| {
2365            state.as_richtext_state_mut().unwrap().len(pos_type)
2366        });
2367
2368        if end > len {
2369            return Err(LoroError::OutOfBound {
2370                pos: end,
2371                len,
2372                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2373            });
2374        }
2375
2376        let (entity_range, skip, missing_style_key, event_start, event_end) = doc_state
2377            .with_state_mut(inner.container_idx, |state| {
2378                let state = state.as_richtext_state_mut().unwrap();
2379                let event_start = state.index_to_event_index(start, pos_type);
2380                let event_end = state.index_to_event_index(end, pos_type);
2381                let (entity_range, styles) =
2382                    state.get_entity_range_and_styles_at_range(start..end, pos_type);
2383
2384                let skip = styles
2385                    .as_ref()
2386                    .map(|styles| styles.has_key_value(&key, &value))
2387                    .unwrap_or(false);
2388                let has_target_style = state.has_style_key_in_entity_range(
2389                    entity_range.clone(),
2390                    &StyleKey::Key(key.clone()),
2391                );
2392                let missing_style_key = is_delete && !has_target_style;
2393
2394                (
2395                    entity_range,
2396                    skip,
2397                    missing_style_key,
2398                    event_start,
2399                    event_end,
2400                )
2401            });
2402
2403        if skip || missing_style_key {
2404            return Ok(());
2405        }
2406
2407        let entity_start = entity_range.start;
2408        let entity_end = entity_range.end;
2409        let style_config = doc_state.config.text_style_config.read();
2410        let flag = if is_delete {
2411            style_config
2412                .get_style_flag_for_unmark(&key)
2413                .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2414        } else {
2415            style_config
2416                .get_style_flag(&key)
2417                .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2418        };
2419
2420        drop(style_config);
2421        drop(doc_state);
2422        txn.apply_local_op(
2423            inner.container_idx,
2424            crate::op::RawOpContent::List(ListOp::StyleStart {
2425                start: entity_start as u32,
2426                end: entity_end as u32,
2427                key: key.clone(),
2428                value: value.clone(),
2429                info: flag,
2430            }),
2431            EventHint::Mark {
2432                start: event_start as u32,
2433                end: event_end as u32,
2434                style: crate::container::richtext::Style { key, data: value },
2435            },
2436            &inner.doc,
2437        )?;
2438
2439        txn.apply_local_op(
2440            inner.container_idx,
2441            crate::op::RawOpContent::List(ListOp::StyleEnd),
2442            EventHint::MarkEnd,
2443            &inner.doc,
2444        )?;
2445
2446        Ok(())
2447    }
2448
2449    pub fn check(&self) {
2450        match &self.inner {
2451            MaybeDetached::Detached(t) => {
2452                let t = t.lock();
2453                t.value.check_consistency_between_content_and_style_ranges();
2454            }
2455            MaybeDetached::Attached(a) => a.with_state(|state| {
2456                state
2457                    .as_richtext_state_mut()
2458                    .unwrap()
2459                    .check_consistency_between_content_and_style_ranges();
2460            }),
2461        }
2462    }
2463
2464    pub fn apply_delta(&self, delta: &[TextDelta]) -> LoroResult<()> {
2465        match &self.inner {
2466            MaybeDetached::Detached(t) => {
2467                let _t = t.lock();
2468                // TODO: implement
2469                Err(LoroError::NotImplemented(
2470                    "`apply_delta` on a detached text container",
2471                ))
2472            }
2473            MaybeDetached::Attached(a) => a.with_txn(|txn| self.apply_delta_with_txn(txn, delta)),
2474        }
2475    }
2476
2477    pub fn apply_delta_with_txn(
2478        &self,
2479        txn: &mut Transaction,
2480        delta: &[TextDelta],
2481    ) -> LoroResult<()> {
2482        let mut index = 0;
2483        struct PendingMark {
2484            start: usize,
2485            end: usize,
2486            attributes: FxHashMap<InternalString, LoroValue>,
2487        }
2488        let mut marks: Vec<PendingMark> = Vec::new();
2489        for d in delta {
2490            match d {
2491                TextDelta::Insert { insert, attributes } => {
2492                    let insert_len = event_len(insert.as_str());
2493                    if insert_len == 0 {
2494                        continue;
2495                    }
2496
2497                    let mut empty_attr = None;
2498                    let attr_ref = attributes.as_ref().unwrap_or_else(|| {
2499                        empty_attr = Some(FxHashMap::default());
2500                        empty_attr.as_ref().unwrap()
2501                    });
2502
2503                    let end = checked_delta_index_end(index, insert_len, self.len_event())?;
2504                    let override_styles = self.insert_with_txn_and_attr(
2505                        txn,
2506                        index,
2507                        insert.as_str(),
2508                        Some(attr_ref),
2509                        PosType::Event,
2510                    )?;
2511
2512                    let mut pending_mark = PendingMark {
2513                        start: index,
2514                        end,
2515                        attributes: FxHashMap::default(),
2516                    };
2517                    for (key, value) in override_styles {
2518                        pending_mark.attributes.insert(key, value);
2519                    }
2520                    marks.push(pending_mark);
2521                    index = end;
2522                }
2523                TextDelta::Delete { delete } => {
2524                    self.delete_with_txn(txn, index, *delete, PosType::Event)?;
2525                }
2526                TextDelta::Retain { attributes, retain } => {
2527                    let end = checked_delta_index_end(index, *retain, self.len_event())?;
2528                    match attributes {
2529                        Some(attr) if !attr.is_empty() => {
2530                            let mut pending_mark = PendingMark {
2531                                start: index,
2532                                end,
2533                                attributes: FxHashMap::default(),
2534                            };
2535                            for (key, value) in attr {
2536                                pending_mark
2537                                    .attributes
2538                                    .insert(key.deref().into(), value.clone());
2539                            }
2540                            marks.push(pending_mark);
2541                        }
2542                        _ => {}
2543                    }
2544                    index = end;
2545                }
2546            }
2547        }
2548
2549        let mut len = match &self.inner {
2550            MaybeDetached::Detached(_) => self.len_event(),
2551            MaybeDetached::Attached(a) => {
2552                a.with_state(|state| state.as_richtext_state_mut().unwrap().len(PosType::Event))
2553            }
2554        };
2555        for pending_mark in marks {
2556            if pending_mark.start >= len {
2557                self.insert_with_txn(
2558                    txn,
2559                    len,
2560                    &"\n".repeat(pending_mark.start - len + 1),
2561                    PosType::Event,
2562                )?;
2563                len = pending_mark.start;
2564            }
2565
2566            for (key, value) in pending_mark.attributes {
2567                self.mark_with_txn(
2568                    txn,
2569                    pending_mark.start,
2570                    pending_mark.end,
2571                    key.deref(),
2572                    value,
2573                    PosType::Event,
2574                )?;
2575            }
2576        }
2577
2578        Ok(())
2579    }
2580
2581    pub fn update(&self, text: &str, options: UpdateOptions) -> Result<(), UpdateTimeoutError> {
2582        let old_str = self.to_string();
2583        let new = text.chars().map(|x| x as u32).collect::<Vec<u32>>();
2584        let old = old_str.chars().map(|x| x as u32).collect::<Vec<u32>>();
2585        diff(
2586            &mut OperateProxy::new(text_update::DiffHook::new(self, &new)),
2587            options,
2588            &old,
2589            &new,
2590        )?;
2591        Ok(())
2592    }
2593
2594    pub fn update_by_line(
2595        &self,
2596        text: &str,
2597        options: UpdateOptions,
2598    ) -> Result<(), UpdateTimeoutError> {
2599        let hook = text_update::DiffHookForLine::new(self, text);
2600        let old_lines = hook.get_old_arr().to_vec();
2601        let new_lines = hook.get_new_arr().to_vec();
2602        diff(
2603            &mut OperateProxy::new(hook),
2604            options,
2605            &old_lines,
2606            &new_lines,
2607        )
2608    }
2609
2610    #[allow(clippy::inherent_to_string)]
2611    pub fn to_string(&self) -> String {
2612        match &self.inner {
2613            MaybeDetached::Detached(t) => t.lock().value.to_string(),
2614            MaybeDetached::Attached(a) => a.get_value().into_string().unwrap().unwrap(),
2615        }
2616    }
2617
2618    pub fn get_cursor(&self, event_index: usize, side: Side) -> Option<Cursor> {
2619        self.get_cursor_internal(event_index, side, true)
2620    }
2621
2622    /// Get the stable position representation for the target pos
2623    pub(crate) fn get_cursor_internal(
2624        &self,
2625        index: usize,
2626        side: Side,
2627        get_by_event_index: bool,
2628    ) -> Option<Cursor> {
2629        match &self.inner {
2630            MaybeDetached::Detached(_) => None,
2631            MaybeDetached::Attached(a) => {
2632                let (id, len, origin_pos) = a.with_state(|s| {
2633                    let s = s.as_richtext_state_mut().unwrap();
2634                    (
2635                        s.get_stable_position(index, get_by_event_index),
2636                        if get_by_event_index {
2637                            s.len_event()
2638                        } else {
2639                            s.len_unicode()
2640                        },
2641                        if get_by_event_index {
2642                            s.event_index_to_unicode_index(index)
2643                        } else {
2644                            index
2645                        },
2646                    )
2647                });
2648
2649                if len == 0 {
2650                    return Some(Cursor {
2651                        id: None,
2652                        container: self.id(),
2653                        side: if side == Side::Middle {
2654                            Side::Left
2655                        } else {
2656                            side
2657                        },
2658                        origin_pos: 0,
2659                    });
2660                }
2661
2662                if len <= index {
2663                    return Some(Cursor {
2664                        id: None,
2665                        container: self.id(),
2666                        side: Side::Right,
2667                        origin_pos: len,
2668                    });
2669                }
2670
2671                let id = id?;
2672                Some(Cursor {
2673                    id: Some(id),
2674                    container: self.id(),
2675                    side,
2676                    origin_pos,
2677                })
2678            }
2679        }
2680    }
2681
2682    pub(crate) fn convert_entity_index_to_event_index(&self, entity_index: usize) -> usize {
2683        match &self.inner {
2684            MaybeDetached::Detached(s) => s.lock().value.entity_index_to_event_index(entity_index),
2685            MaybeDetached::Attached(a) => {
2686                let mut pos = 0;
2687                a.with_state(|s| {
2688                    let s = s.as_richtext_state_mut().unwrap();
2689                    pos = s.entity_index_to_event_index(entity_index);
2690                });
2691                pos
2692            }
2693        }
2694    }
2695
2696    pub fn get_delta(&self) -> Vec<TextDelta> {
2697        match &self.inner {
2698            MaybeDetached::Detached(s) => {
2699                let mut delta = Vec::new();
2700                for span in s.lock().value.iter() {
2701                    if span.text.as_str().is_empty() {
2702                        continue;
2703                    }
2704
2705                    let next_attr = span.attributes.to_option_map();
2706                    match delta.last_mut() {
2707                        Some(TextDelta::Insert { insert, attributes })
2708                            if &next_attr == attributes =>
2709                        {
2710                            insert.push_str(span.text.as_str());
2711                            continue;
2712                        }
2713                        _ => {}
2714                    }
2715
2716                    delta.push(TextDelta::Insert {
2717                        insert: span.text.as_str().to_string(),
2718                        attributes: next_attr,
2719                    })
2720                }
2721                delta
2722            }
2723            MaybeDetached::Attached(_a) => self
2724                .with_state(|state| {
2725                    let state = state.as_richtext_state_mut().unwrap();
2726                    Ok(state.get_delta())
2727                })
2728                .unwrap(),
2729        }
2730    }
2731
2732    pub fn is_deleted(&self) -> bool {
2733        match &self.inner {
2734            MaybeDetached::Detached(_) => false,
2735            MaybeDetached::Attached(a) => a.is_deleted(),
2736        }
2737    }
2738
2739    pub fn push_str(&self, s: &str) -> LoroResult<()> {
2740        self.insert_utf8(self.len_utf8(), s)
2741    }
2742
2743    pub fn clear(&self) -> LoroResult<()> {
2744        match &self.inner {
2745            MaybeDetached::Detached(mutex) => {
2746                let mut t = mutex.lock();
2747                let len = t.value.len_unicode();
2748                let ranges = t.value.get_text_entity_ranges(0, len, PosType::Unicode)?;
2749                for range in ranges.iter().rev() {
2750                    t.value
2751                        .drain_by_entity_index(range.entity_start, range.entity_len(), None);
2752                }
2753                Ok(())
2754            }
2755            MaybeDetached::Attached(a) => a.with_txn(|txn| {
2756                let len = a.with_state(|s| s.as_richtext_state_mut().unwrap().len_unicode());
2757                self.delete_with_txn_inline(txn, 0, len, PosType::Unicode)
2758            }),
2759        }
2760    }
2761
2762    /// Convert a position `index` from one coordinate system to another.
2763    ///
2764    /// Supported `PosType` conversions: `Event`, `Unicode`, `Utf16`, and `Bytes`.
2765    /// Returns `None` if the index is out of bounds or the conversion is unsupported.
2766    pub fn convert_pos(&self, index: usize, from: PosType, to: PosType) -> Option<usize> {
2767        if from == to {
2768            return Some(index);
2769        }
2770
2771        if matches!(from, PosType::Entity) || matches!(to, PosType::Entity) {
2772            return None;
2773        }
2774
2775        // Normalize to event + unicode indices for the given position.
2776        let (event_index, unicode_index) = match &self.inner {
2777            MaybeDetached::Detached(t) => {
2778                let t = t.lock();
2779                if index > t.value.len(from) {
2780                    return None;
2781                }
2782                let event_index = if from == PosType::Event {
2783                    index
2784                } else {
2785                    t.value.index_to_event_index(index, from)
2786                };
2787                let unicode_index = if from == PosType::Unicode {
2788                    index
2789                } else {
2790                    t.value.event_index_to_unicode_index(event_index)
2791                };
2792                (event_index, unicode_index)
2793            }
2794            MaybeDetached::Attached(a) if a.has_decoded_state() => {
2795                let res: Option<(usize, usize)> = a.with_state(|state| {
2796                    let state = state.as_richtext_state_mut().unwrap();
2797                    if index > state.len(from) {
2798                        return None;
2799                    }
2800
2801                    let event_index = if from == PosType::Event {
2802                        index
2803                    } else {
2804                        state.index_to_event_index(index, from)
2805                    };
2806                    let unicode_index = if from == PosType::Unicode {
2807                        index
2808                    } else {
2809                        state.event_index_to_unicode_index(event_index)
2810                    };
2811                    Some((event_index, unicode_index))
2812                });
2813
2814                res?
2815            }
2816            MaybeDetached::Attached(a) => {
2817                let value = a.get_value();
2818                let s = value.as_string().unwrap();
2819                let unicode_index = text_pos_to_unicode(s, index, from)?;
2820                let event_index = unicode_to_text_pos(s, unicode_index, PosType::Event)?;
2821                (event_index, unicode_index)
2822            }
2823        };
2824
2825        let result = match to {
2826            PosType::Unicode => Some(unicode_index),
2827            PosType::Event => Some(event_index),
2828            PosType::Bytes | PosType::Utf16 => {
2829                // Map the event-index position onto the target coordinate via the
2830                // rope's prefix caches. This is O(log n); materializing the prefix
2831                // string would be O(n) and makes repeated edits O(n^2).
2832                match &self.inner {
2833                    MaybeDetached::Detached(t) => {
2834                        let t = t.lock();
2835                        if event_index > t.value.len_event() {
2836                            return None;
2837                        }
2838                        Some(t.value.event_index_to_index(event_index, to))
2839                    }
2840                    MaybeDetached::Attached(a) if a.has_decoded_state() => a.with_state(|state| {
2841                        let state = state.as_richtext_state_mut().unwrap();
2842                        if event_index > state.len_event() {
2843                            return None;
2844                        }
2845                        Some(state.event_index_to_index(event_index, to))
2846                    }),
2847                    MaybeDetached::Attached(a) => {
2848                        let value = a.get_value();
2849                        let s = value.as_string().unwrap();
2850                        unicode_to_text_pos(s, unicode_index, to)
2851                    }
2852                }
2853            }
2854            PosType::Entity => None,
2855        };
2856        result
2857    }
2858}
2859
2860fn event_len(s: &str) -> usize {
2861    if cfg!(feature = "wasm") {
2862        count_utf16_len(s.as_bytes())
2863    } else {
2864        s.chars().count()
2865    }
2866}
2867
2868fn text_len(s: &str, pos_type: PosType) -> Option<usize> {
2869    Some(match pos_type {
2870        PosType::Bytes => s.len(),
2871        PosType::Unicode => s.chars().count(),
2872        PosType::Utf16 => count_utf16_len(s.as_bytes()),
2873        PosType::Event => event_len(s),
2874        PosType::Entity => return None,
2875    })
2876}
2877
2878fn text_pos_to_unicode(s: &str, index: usize, pos_type: PosType) -> Option<usize> {
2879    match pos_type {
2880        PosType::Unicode => (index <= s.chars().count()).then_some(index),
2881        PosType::Bytes => {
2882            if index > s.len() {
2883                None
2884            } else {
2885                Some(
2886                    s.char_indices()
2887                        .take_while(|(pos, c)| *pos + c.len_utf8() <= index)
2888                        .count(),
2889                )
2890            }
2891        }
2892        PosType::Utf16 => utf16_to_unicode_pos(s, index),
2893        PosType::Event if cfg!(feature = "wasm") => utf16_to_unicode_pos(s, index),
2894        PosType::Event => (index <= s.chars().count()).then_some(index),
2895        PosType::Entity => None,
2896    }
2897}
2898
2899fn unicode_to_text_pos(s: &str, index: usize, pos_type: PosType) -> Option<usize> {
2900    match pos_type {
2901        PosType::Unicode => (index <= s.chars().count()).then_some(index),
2902        PosType::Bytes => unicode_to_byte_pos(s, index),
2903        PosType::Utf16 => unicode_to_utf16_pos(s, index),
2904        PosType::Event if cfg!(feature = "wasm") => unicode_to_utf16_pos(s, index),
2905        PosType::Event => (index <= s.chars().count()).then_some(index),
2906        PosType::Entity => None,
2907    }
2908}
2909
2910fn unicode_to_byte_pos(s: &str, index: usize) -> Option<usize> {
2911    if index == 0 {
2912        return Some(0);
2913    }
2914
2915    let mut unicode_pos = 0;
2916    for (byte_pos, _) in s.char_indices() {
2917        if unicode_pos == index {
2918            return Some(byte_pos);
2919        }
2920        unicode_pos += 1;
2921    }
2922
2923    (unicode_pos == index).then_some(s.len())
2924}
2925
2926fn unicode_to_utf16_pos(s: &str, index: usize) -> Option<usize> {
2927    let mut unicode_pos = 0;
2928    let mut utf16_pos = 0;
2929    if index == 0 {
2930        return Some(0);
2931    }
2932
2933    for c in s.chars() {
2934        unicode_pos += 1;
2935        utf16_pos += c.len_utf16();
2936        if unicode_pos == index {
2937            return Some(utf16_pos);
2938        }
2939    }
2940
2941    (unicode_pos == index).then_some(utf16_pos)
2942}
2943
2944fn utf16_to_unicode_pos(s: &str, index: usize) -> Option<usize> {
2945    let mut unicode_pos = 0;
2946    let mut utf16_pos = 0;
2947    if index == 0 {
2948        return Some(0);
2949    }
2950
2951    for c in s.chars() {
2952        let next_utf16_pos = utf16_pos + c.len_utf16();
2953        if index < next_utf16_pos {
2954            return Some(unicode_pos);
2955        }
2956        if index == next_utf16_pos {
2957            return Some(unicode_pos + 1);
2958        }
2959        utf16_pos = next_utf16_pos;
2960        unicode_pos += 1;
2961    }
2962
2963    (index == utf16_pos).then_some(unicode_pos)
2964}
2965
2966fn text_boundary_error(pos: usize, pos_type: PosType) -> LoroError {
2967    match pos_type {
2968        PosType::Bytes => LoroError::UTF8InUnicodeCodePoint { pos },
2969        PosType::Utf16 => LoroError::UTF16InUnicodeCodePoint { pos },
2970        PosType::Event if cfg!(feature = "wasm") => LoroError::UTF16InUnicodeCodePoint { pos },
2971        _ => LoroError::OutOfBound {
2972            pos,
2973            len: 0,
2974            info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2975        },
2976    }
2977}
2978
2979fn text_char_at(s: &str, pos: usize, pos_type: PosType) -> LoroResult<char> {
2980    let len = text_len(s, pos_type).unwrap_or(0);
2981    if pos >= len {
2982        return Err(LoroError::OutOfBound {
2983            pos,
2984            len,
2985            info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2986        });
2987    }
2988
2989    let unicode_pos =
2990        text_pos_to_unicode(s, pos, pos_type).ok_or_else(|| text_boundary_error(pos, pos_type))?;
2991    s.chars().nth(unicode_pos).ok_or(LoroError::OutOfBound {
2992        pos,
2993        len,
2994        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2995    })
2996}
2997
2998fn text_slice(s: &str, start: usize, end: usize, pos_type: PosType) -> LoroResult<String> {
2999    if end < start {
3000        return Err(LoroError::EndIndexLessThanStartIndex { start, end });
3001    }
3002    if start == end {
3003        return Ok(String::new());
3004    }
3005
3006    let len = text_len(s, pos_type).unwrap_or(0);
3007    if end > len {
3008        return Err(LoroError::OutOfBound {
3009            pos: end,
3010            len,
3011            info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3012        });
3013    }
3014
3015    let start = text_pos_to_unicode(s, start, pos_type)
3016        .ok_or_else(|| text_boundary_error(start, pos_type))?;
3017    let end =
3018        text_pos_to_unicode(s, end, pos_type).ok_or_else(|| text_boundary_error(end, pos_type))?;
3019    let start = unicode_to_byte_pos(s, start).expect("unicode index must map to a byte boundary");
3020    let end = unicode_to_byte_pos(s, end).expect("unicode index must map to a byte boundary");
3021    Ok(s[start..end].to_string())
3022}
3023
3024impl ListHandler {
3025    /// Create a new container that is detached from the document.
3026    /// The edits on a detached container will not be persisted.
3027    /// To attach the container to the document, please insert it into an attached container.
3028    pub fn new_detached() -> Self {
3029        Self {
3030            inner: MaybeDetached::new_detached(Vec::new()),
3031        }
3032    }
3033
3034    pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3035        match &self.inner {
3036            MaybeDetached::Detached(l) => {
3037                let mut list = l.lock();
3038                let len = list.value.len();
3039                if pos > len {
3040                    return Err(LoroError::OutOfBound {
3041                        pos,
3042                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3043                        len,
3044                    });
3045                }
3046                let value = v.into();
3047                ensure_no_regular_container_value(&value)?;
3048                list.value.insert(pos, ValueOrHandler::Value(value));
3049                Ok(())
3050            }
3051            MaybeDetached::Attached(a) => {
3052                a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
3053            }
3054        }
3055    }
3056
3057    pub fn insert_with_txn(
3058        &self,
3059        txn: &mut Transaction,
3060        pos: usize,
3061        v: LoroValue,
3062    ) -> LoroResult<()> {
3063        if pos > self.len() {
3064            return Err(LoroError::OutOfBound {
3065                pos,
3066                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3067                len: self.len(),
3068            });
3069        }
3070
3071        let inner = self.inner.try_attached_state()?;
3072        ensure_no_regular_container_value(&v)?;
3073
3074        txn.apply_local_op(
3075            inner.container_idx,
3076            crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3077                slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3078                pos,
3079            }),
3080            EventHint::InsertList { len: 1, pos },
3081            &inner.doc,
3082        )
3083    }
3084
3085    pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
3086        match &self.inner {
3087            MaybeDetached::Detached(l) => {
3088                let mut list = l.lock();
3089                let value = v.into();
3090                ensure_no_regular_container_value(&value)?;
3091                list.value.push(ValueOrHandler::Value(value));
3092                Ok(())
3093            }
3094            MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v.into())),
3095        }
3096    }
3097
3098    pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
3099        let pos = self.len();
3100        self.insert_with_txn(txn, pos, v)
3101    }
3102
3103    pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
3104        match &self.inner {
3105            MaybeDetached::Detached(l) => {
3106                let mut list = l.lock();
3107                Ok(list.value.pop().map(|v| v.to_value()))
3108            }
3109            MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
3110        }
3111    }
3112
3113    pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
3114        let len = self.len();
3115        if len == 0 {
3116            return Ok(None);
3117        }
3118
3119        let v = self.get(len - 1);
3120        self.delete_with_txn(txn, len - 1, 1)?;
3121        Ok(v)
3122    }
3123
3124    pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3125        match &self.inner {
3126            MaybeDetached::Detached(l) => {
3127                let mut list = l.lock();
3128                if pos > list.value.len() {
3129                    return Err(LoroError::OutOfBound {
3130                        pos,
3131                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3132                        len: list.value.len(),
3133                    });
3134                }
3135                list.value
3136                    .insert(pos, ValueOrHandler::Handler(child.to_handler()));
3137                Ok(child)
3138            }
3139            MaybeDetached::Attached(a) => {
3140                a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
3141            }
3142        }
3143    }
3144
3145    pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
3146        self.insert_container(self.len(), child)
3147    }
3148
3149    pub fn insert_container_with_txn<H: HandlerTrait>(
3150        &self,
3151        txn: &mut Transaction,
3152        pos: usize,
3153        child: H,
3154    ) -> LoroResult<H> {
3155        if pos > self.len() {
3156            return Err(LoroError::OutOfBound {
3157                pos,
3158                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3159                len: self.len(),
3160            });
3161        }
3162
3163        let inner = self.inner.try_attached_state()?;
3164        let id = txn.next_id();
3165        let container_id = ContainerID::new_normal(id, child.kind());
3166        let v = LoroValue::Container(container_id.clone());
3167        txn.apply_local_op(
3168            inner.container_idx,
3169            crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3170                slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3171                pos,
3172            }),
3173            EventHint::InsertList { len: 1, pos },
3174            &inner.doc,
3175        )?;
3176        let ans = child.attach(txn, inner, container_id)?;
3177        Ok(ans)
3178    }
3179
3180    pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3181        match &self.inner {
3182            MaybeDetached::Detached(l) => {
3183                let mut list = l.lock();
3184                let end = checked_range_end(
3185                    pos,
3186                    len,
3187                    list.value.len(),
3188                    format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3189                )?;
3190                list.value.drain(pos..end);
3191                Ok(())
3192            }
3193            MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
3194        }
3195    }
3196
3197    pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
3198        if len == 0 {
3199            return Ok(());
3200        }
3201
3202        let list_len = self.len();
3203        let end = checked_range_end(
3204            pos,
3205            len,
3206            list_len,
3207            format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3208        )?;
3209
3210        let inner = self.inner.try_attached_state()?;
3211        let ids: Vec<_> = inner.with_state(|state| {
3212            let list = state.as_list_state().unwrap();
3213            (pos..end).map(|i| list.get_id_at(i).unwrap()).collect()
3214        });
3215
3216        for id in ids.into_iter() {
3217            txn.apply_local_op(
3218                inner.container_idx,
3219                crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
3220                    id.id(),
3221                    pos as isize,
3222                    1,
3223                ))),
3224                EventHint::DeleteList(DeleteSpan::new(pos as isize, 1)),
3225                &inner.doc,
3226            )?;
3227        }
3228
3229        Ok(())
3230    }
3231
3232    pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
3233        match &self.inner {
3234            MaybeDetached::Detached(l) => {
3235                let list = l.lock();
3236                let value = list.value.get(index).ok_or(LoroError::OutOfBound {
3237                    pos: index,
3238                    info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3239                    len: list.value.len(),
3240                })?;
3241                match value {
3242                    ValueOrHandler::Handler(h) => Ok(h.clone()),
3243                    _ => Err(LoroError::ArgErr(
3244                        format!(
3245                            "Expected container at index {}, but found {:?}",
3246                            index, value
3247                        )
3248                        .into_boxed_str(),
3249                    )),
3250                }
3251            }
3252            MaybeDetached::Attached(_) => {
3253                let Some(value) = self.get_(index) else {
3254                    return Err(LoroError::OutOfBound {
3255                        pos: index,
3256                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3257                        len: self.len(),
3258                    });
3259                };
3260                match value {
3261                    ValueOrHandler::Handler(handler) => Ok(handler),
3262                    ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
3263                        format!(
3264                            "Expected container at index {}, but found {:?}",
3265                            index, value
3266                        )
3267                        .into_boxed_str(),
3268                    )),
3269                }
3270            }
3271        }
3272    }
3273
3274    pub fn len(&self) -> usize {
3275        match &self.inner {
3276            MaybeDetached::Detached(l) => l.lock().value.len(),
3277            MaybeDetached::Attached(a) => {
3278                a.with_doc_state(|state| state.get_list_len(a.container_idx))
3279            }
3280        }
3281    }
3282
3283    pub fn is_empty(&self) -> bool {
3284        self.len() == 0
3285    }
3286
3287    pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
3288        let inner = self.inner.try_attached_state()?;
3289        Ok(inner.with_doc_state(|state| {
3290            state.get_container_deep_value_with_id(inner.container_idx, None)
3291        }))
3292    }
3293
3294    pub fn get(&self, index: usize) -> Option<LoroValue> {
3295        match &self.inner {
3296            MaybeDetached::Detached(l) => l.lock().value.get(index).map(|x| x.to_value()),
3297            MaybeDetached::Attached(a) => {
3298                a.with_doc_state(|state| state.get_list_value_at(a.container_idx, index))
3299            }
3300        }
3301    }
3302
3303    /// Get value at given index, if it's a container, return a handler to the container
3304    pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
3305        match &self.inner {
3306            MaybeDetached::Detached(l) => {
3307                let l = l.lock();
3308                l.value.get(index).cloned()
3309            }
3310            MaybeDetached::Attached(inner) => {
3311                let value = inner
3312                    .with_doc_state(|state| state.get_list_value_at(inner.container_idx, index));
3313                value.map(|value| value_to_value_or_handler(inner, value))
3314            }
3315        }
3316    }
3317
3318    pub fn for_each<I>(&self, mut f: I)
3319    where
3320        I: FnMut(ValueOrHandler),
3321    {
3322        match &self.inner {
3323            MaybeDetached::Detached(l) => {
3324                let l = l.lock();
3325                for v in l.value.iter() {
3326                    f(v.clone())
3327                }
3328            }
3329            MaybeDetached::Attached(inner) => {
3330                let temp = inner.with_doc_state(|state| {
3331                    state
3332                        .get_list_values(inner.container_idx)
3333                        .into_iter()
3334                        .map(|value| value_to_value_or_handler(inner, value))
3335                        .collect::<Vec<_>>()
3336                });
3337                for v in temp.into_iter() {
3338                    f(v);
3339                }
3340            }
3341        }
3342    }
3343
3344    pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
3345        match &self.inner {
3346            MaybeDetached::Detached(_) => None,
3347            MaybeDetached::Attached(a) => {
3348                let (id, len) = a.with_state(|s| {
3349                    let l = s.as_list_state().unwrap();
3350                    (l.get_id_at(pos), l.len())
3351                });
3352
3353                if len == 0 {
3354                    return Some(Cursor {
3355                        id: None,
3356                        container: self.id(),
3357                        side: if side == Side::Middle {
3358                            Side::Left
3359                        } else {
3360                            side
3361                        },
3362                        origin_pos: 0,
3363                    });
3364                }
3365
3366                if len <= pos {
3367                    return Some(Cursor {
3368                        id: None,
3369                        container: self.id(),
3370                        side: Side::Right,
3371                        origin_pos: len,
3372                    });
3373                }
3374
3375                let id = id?;
3376                Some(Cursor {
3377                    id: Some(id.id()),
3378                    container: self.id(),
3379                    side,
3380                    origin_pos: pos,
3381                })
3382            }
3383        }
3384    }
3385
3386    fn apply_delta(
3387        &self,
3388        delta: loro_delta::DeltaRope<
3389            loro_delta::array_vec::ArrayVec<ValueOrHandler, 8>,
3390            crate::event::ListDeltaMeta,
3391        >,
3392        on_container_remap: &mut dyn FnMut(ContainerID, ContainerID),
3393    ) -> LoroResult<()> {
3394        match &self.inner {
3395            MaybeDetached::Detached(_) => unimplemented!(),
3396            MaybeDetached::Attached(_) => {
3397                let mut index = 0;
3398                for item in delta.iter() {
3399                    match item {
3400                        loro_delta::DeltaItem::Retain { len, .. } => {
3401                            index += len;
3402                        }
3403                        loro_delta::DeltaItem::Replace { value, delete, .. } => {
3404                            if *delete > 0 {
3405                                self.delete(index, *delete)?;
3406                            }
3407
3408                            for v in value.iter() {
3409                                match v {
3410                                    ValueOrHandler::Value(LoroValue::Container(old_id)) => {
3411                                        let new_h = self.insert_container(
3412                                            index,
3413                                            Handler::new_unattached(old_id.container_type()),
3414                                        )?;
3415                                        let new_id = new_h.id();
3416                                        on_container_remap(old_id.clone(), new_id);
3417                                    }
3418                                    ValueOrHandler::Handler(h) => {
3419                                        let old_id = h.id();
3420                                        let new_h = self.insert_container(
3421                                            index,
3422                                            Handler::new_unattached(old_id.container_type()),
3423                                        )?;
3424                                        let new_id = new_h.id();
3425                                        on_container_remap(old_id, new_id);
3426                                    }
3427                                    ValueOrHandler::Value(v) => {
3428                                        self.insert(index, v.clone())?;
3429                                    }
3430                                }
3431
3432                                index += 1;
3433                            }
3434                        }
3435                    }
3436                }
3437            }
3438        }
3439
3440        Ok(())
3441    }
3442
3443    pub fn is_deleted(&self) -> bool {
3444        match &self.inner {
3445            MaybeDetached::Detached(_) => false,
3446            MaybeDetached::Attached(a) => a.is_deleted(),
3447        }
3448    }
3449
3450    pub fn clear(&self) -> LoroResult<()> {
3451        match &self.inner {
3452            MaybeDetached::Detached(l) => {
3453                let mut l = l.lock();
3454                l.value.clear();
3455                Ok(())
3456            }
3457            MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
3458        }
3459    }
3460
3461    pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
3462        self.delete_with_txn(txn, 0, self.len())
3463    }
3464
3465    pub fn get_id_at(&self, pos: usize) -> Option<ID> {
3466        match &self.inner {
3467            MaybeDetached::Detached(_) => None,
3468            MaybeDetached::Attached(a) => a.with_state(|state| {
3469                state
3470                    .as_list_state()
3471                    .unwrap()
3472                    .get_id_at(pos)
3473                    .map(|x| x.id())
3474            }),
3475        }
3476    }
3477}
3478
3479impl MovableListHandler {
3480    pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3481        match &self.inner {
3482            MaybeDetached::Detached(d) => {
3483                let mut d = d.lock();
3484                if pos > d.value.len() {
3485                    return Err(LoroError::OutOfBound {
3486                        pos,
3487                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3488                        len: d.value.len(),
3489                    });
3490                }
3491                let value = v.into();
3492                ensure_no_regular_container_value(&value)?;
3493                d.value.insert(pos, ValueOrHandler::Value(value));
3494                Ok(())
3495            }
3496            MaybeDetached::Attached(a) => {
3497                a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
3498            }
3499        }
3500    }
3501
3502    #[instrument(skip_all)]
3503    pub fn insert_with_txn(
3504        &self,
3505        txn: &mut Transaction,
3506        pos: usize,
3507        v: LoroValue,
3508    ) -> LoroResult<()> {
3509        if pos > self.len() {
3510            return Err(LoroError::OutOfBound {
3511                pos,
3512                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3513                len: self.len(),
3514            });
3515        }
3516
3517        ensure_no_regular_container_value(&v)?;
3518
3519        let op_index = self.with_state(|state| {
3520            let list = state.as_movable_list_state().unwrap();
3521            Ok(list
3522                .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3523                .unwrap())
3524        })?;
3525
3526        let inner = self.inner.try_attached_state()?;
3527        txn.apply_local_op(
3528            inner.container_idx,
3529            crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3530                slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3531                pos: op_index,
3532            }),
3533            EventHint::InsertList { len: 1, pos },
3534            &inner.doc,
3535        )
3536    }
3537
3538    #[inline]
3539    pub fn mov(&self, from: usize, to: usize) -> LoroResult<()> {
3540        match &self.inner {
3541            MaybeDetached::Detached(d) => {
3542                let mut d = d.lock();
3543                if from >= d.value.len() {
3544                    return Err(LoroError::OutOfBound {
3545                        pos: from,
3546                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3547                        len: d.value.len(),
3548                    });
3549                }
3550                if to >= d.value.len() {
3551                    return Err(LoroError::OutOfBound {
3552                        pos: to,
3553                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3554                        len: d.value.len(),
3555                    });
3556                }
3557                let v = d.value.remove(from);
3558                d.value.insert(to, v);
3559                Ok(())
3560            }
3561            MaybeDetached::Attached(a) => a.with_txn(|txn| self.move_with_txn(txn, from, to)),
3562        }
3563    }
3564
3565    /// Move element from `from` to `to`. After this op, elem will be at pos `to`.
3566    #[instrument(skip_all)]
3567    pub fn move_with_txn(&self, txn: &mut Transaction, from: usize, to: usize) -> LoroResult<()> {
3568        if from == to {
3569            return Ok(());
3570        }
3571
3572        if from >= self.len() {
3573            return Err(LoroError::OutOfBound {
3574                pos: from,
3575                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3576                len: self.len(),
3577            });
3578        }
3579
3580        if to >= self.len() {
3581            return Err(LoroError::OutOfBound {
3582                pos: to,
3583                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3584                len: self.len(),
3585            });
3586        }
3587
3588        let (op_from, op_to, elem_id, value) = self.with_state(|state| {
3589            let list = state.as_movable_list_state().unwrap();
3590            let (elem_id, elem) = list
3591                .get_elem_at_given_pos(from, IndexType::ForUser)
3592                .unwrap();
3593            Ok((
3594                list.convert_index(from, IndexType::ForUser, IndexType::ForOp)
3595                    .unwrap(),
3596                list.convert_index(to, IndexType::ForUser, IndexType::ForOp)
3597                    .unwrap(),
3598                elem_id,
3599                elem.value().clone(),
3600            ))
3601        })?;
3602
3603        let inner = self.inner.try_attached_state()?;
3604        txn.apply_local_op(
3605            inner.container_idx,
3606            crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Move {
3607                from: op_from as u32,
3608                to: op_to as u32,
3609                elem_id: elem_id.to_id(),
3610            }),
3611            EventHint::Move {
3612                value,
3613                from: from as u32,
3614                to: to as u32,
3615            },
3616            &inner.doc,
3617        )
3618    }
3619
3620    pub fn push(&self, v: LoroValue) -> LoroResult<()> {
3621        match &self.inner {
3622            MaybeDetached::Detached(d) => {
3623                let mut d = d.lock();
3624                d.value.push(v.into());
3625                Ok(())
3626            }
3627            MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v)),
3628        }
3629    }
3630
3631    pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
3632        let pos = self.len();
3633        self.insert_with_txn(txn, pos, v)
3634    }
3635
3636    pub fn pop_(&self) -> LoroResult<Option<ValueOrHandler>> {
3637        match &self.inner {
3638            MaybeDetached::Detached(d) => {
3639                let mut d = d.lock();
3640                Ok(d.value.pop())
3641            }
3642            MaybeDetached::Attached(a) => {
3643                if self.is_empty() {
3644                    return Ok(None);
3645                }
3646                let last = self.len() - 1;
3647                let ans = self.get_(last);
3648                a.with_txn(|txn| self.pop_with_txn(txn))?;
3649                Ok(ans)
3650            }
3651        }
3652    }
3653
3654    pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
3655        match &self.inner {
3656            MaybeDetached::Detached(a) => {
3657                let mut a = a.lock();
3658                Ok(a.value.pop().map(|x| x.to_value()))
3659            }
3660            MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
3661        }
3662    }
3663
3664    pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
3665        let len = self.len();
3666        if len == 0 {
3667            return Ok(None);
3668        }
3669
3670        let v = self.get(len - 1);
3671        self.delete_with_txn(txn, len - 1, 1)?;
3672        Ok(v)
3673    }
3674
3675    pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3676        match &self.inner {
3677            MaybeDetached::Detached(d) => {
3678                let mut d = d.lock();
3679                if pos > d.value.len() {
3680                    return Err(LoroError::OutOfBound {
3681                        pos,
3682                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3683                        len: d.value.len(),
3684                    });
3685                }
3686                d.value
3687                    .insert(pos, ValueOrHandler::Handler(child.to_handler()));
3688                Ok(child)
3689            }
3690            MaybeDetached::Attached(a) => {
3691                a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
3692            }
3693        }
3694    }
3695
3696    pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
3697        self.insert_container(self.len(), child)
3698    }
3699
3700    pub fn insert_container_with_txn<H: HandlerTrait>(
3701        &self,
3702        txn: &mut Transaction,
3703        pos: usize,
3704        child: H,
3705    ) -> LoroResult<H> {
3706        if pos > self.len() {
3707            return Err(LoroError::OutOfBound {
3708                pos,
3709                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3710                len: self.len(),
3711            });
3712        }
3713
3714        let op_index = self.with_state(|state| {
3715            let list = state.as_movable_list_state().unwrap();
3716            Ok(list
3717                .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3718                .unwrap())
3719        })?;
3720
3721        let id = txn.next_id();
3722        let container_id = ContainerID::new_normal(id, child.kind());
3723        let v = LoroValue::Container(container_id.clone());
3724        let inner = self.inner.try_attached_state()?;
3725        txn.apply_local_op(
3726            inner.container_idx,
3727            crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3728                slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3729                pos: op_index,
3730            }),
3731            EventHint::InsertList { len: 1, pos },
3732            &inner.doc,
3733        )?;
3734        child.attach(txn, inner, container_id)
3735    }
3736
3737    pub fn set(&self, index: usize, value: impl Into<LoroValue>) -> LoroResult<()> {
3738        match &self.inner {
3739            MaybeDetached::Detached(d) => {
3740                let mut d = d.lock();
3741                if index >= d.value.len() {
3742                    return Err(LoroError::OutOfBound {
3743                        pos: index,
3744                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3745                        len: d.value.len(),
3746                    });
3747                }
3748                let value = value.into();
3749                ensure_no_regular_container_value(&value)?;
3750                d.value[index] = ValueOrHandler::Value(value);
3751                Ok(())
3752            }
3753            MaybeDetached::Attached(a) => {
3754                a.with_txn(|txn| self.set_with_txn(txn, index, value.into()))
3755            }
3756        }
3757    }
3758
3759    pub fn set_with_txn(
3760        &self,
3761        txn: &mut Transaction,
3762        index: usize,
3763        value: LoroValue,
3764    ) -> LoroResult<()> {
3765        if index >= self.len() {
3766            return Err(LoroError::OutOfBound {
3767                pos: index,
3768                info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3769                len: self.len(),
3770            });
3771        }
3772
3773        let inner = self.inner.try_attached_state()?;
3774        let Some(elem_id) = self.with_state(|state| {
3775            let list = state.as_movable_list_state().unwrap();
3776            Ok(list.get_elem_id_at(index, IndexType::ForUser))
3777        })?
3778        else {
3779            unreachable!()
3780        };
3781        ensure_no_regular_container_value(&value)?;
3782
3783        let op = crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3784            elem_id: elem_id.to_id(),
3785            value: value.clone(),
3786        });
3787
3788        let hint = EventHint::SetList { index, value };
3789        txn.apply_local_op(inner.container_idx, op, hint, &inner.doc)
3790    }
3791
3792    pub fn set_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3793        match &self.inner {
3794            MaybeDetached::Detached(d) => {
3795                let mut d = d.lock();
3796                if pos >= d.value.len() {
3797                    return Err(LoroError::OutOfBound {
3798                        pos,
3799                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3800                        len: d.value.len(),
3801                    });
3802                }
3803                d.value[pos] = ValueOrHandler::Handler(child.to_handler());
3804                Ok(child)
3805            }
3806            MaybeDetached::Attached(a) => {
3807                a.with_txn(|txn| self.set_container_with_txn(txn, pos, child))
3808            }
3809        }
3810    }
3811
3812    pub fn set_container_with_txn<H: HandlerTrait>(
3813        &self,
3814        txn: &mut Transaction,
3815        pos: usize,
3816        child: H,
3817    ) -> LoroResult<H> {
3818        let id = txn.next_id();
3819        let container_id = ContainerID::new_normal(id, child.kind());
3820        let v = LoroValue::Container(container_id.clone());
3821        let Some(elem_id) = self.with_state(|state| {
3822            let list = state.as_movable_list_state().unwrap();
3823            Ok(list.get_elem_id_at(pos, IndexType::ForUser))
3824        })?
3825        else {
3826            let len = self.len();
3827            if pos >= len {
3828                return Err(LoroError::OutOfBound {
3829                    pos,
3830                    len,
3831                    info: "".into(),
3832                });
3833            } else {
3834                unreachable!()
3835            }
3836        };
3837        let inner = self.inner.try_attached_state()?;
3838        txn.apply_local_op(
3839            inner.container_idx,
3840            crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3841                elem_id: elem_id.to_id(),
3842                value: v.clone(),
3843            }),
3844            EventHint::SetList {
3845                index: pos,
3846                value: v,
3847            },
3848            &inner.doc,
3849        )?;
3850
3851        child.attach(txn, inner, container_id)
3852    }
3853
3854    pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3855        match &self.inner {
3856            MaybeDetached::Detached(d) => {
3857                let mut d = d.lock();
3858                let end = checked_range_end(
3859                    pos,
3860                    len,
3861                    d.value.len(),
3862                    format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3863                )?;
3864                d.value.drain(pos..end);
3865                Ok(())
3866            }
3867            MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
3868        }
3869    }
3870
3871    #[instrument(skip_all)]
3872    pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
3873        if len == 0 {
3874            return Ok(());
3875        }
3876
3877        let list_len = self.len();
3878        let end = checked_range_end(
3879            pos,
3880            len,
3881            list_len,
3882            format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3883        )?;
3884
3885        let (ids, new_poses) = self.with_state(|state| {
3886            let list = state.as_movable_list_state().unwrap();
3887            let ids: Vec<_> = (pos..end)
3888                .map(|i| list.get_list_id_at(i, IndexType::ForUser).unwrap())
3889                .collect();
3890            let poses: Vec<_> = (pos..end)
3891                // need to -i because we delete the previous ones
3892                .map(|user_index| {
3893                    let op_index = list
3894                        .convert_index(user_index, IndexType::ForUser, IndexType::ForOp)
3895                        .unwrap();
3896                    assert!(op_index >= user_index);
3897                    op_index - (user_index - pos)
3898                })
3899                .collect();
3900            Ok((ids, poses))
3901        })?;
3902
3903        loro_common::info!(?pos, ?len, ?ids, ?new_poses, "delete_with_txn");
3904        let user_pos = pos;
3905        let inner = self.inner.try_attached_state()?;
3906        for (id, op_pos) in ids.into_iter().zip(new_poses.into_iter()) {
3907            txn.apply_local_op(
3908                inner.container_idx,
3909                crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
3910                    id,
3911                    op_pos as isize,
3912                    1,
3913                ))),
3914                EventHint::DeleteList(DeleteSpan::new(user_pos as isize, 1)),
3915                &inner.doc,
3916            )?;
3917        }
3918
3919        Ok(())
3920    }
3921
3922    pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
3923        match &self.inner {
3924            MaybeDetached::Detached(l) => {
3925                let list = l.lock();
3926                let value = list.value.get(index).ok_or(LoroError::OutOfBound {
3927                    pos: index,
3928                    info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3929                    len: list.value.len(),
3930                })?;
3931                match value {
3932                    ValueOrHandler::Handler(h) => Ok(h.clone()),
3933                    _ => Err(LoroError::ArgErr(
3934                        format!(
3935                            "Expected container at index {}, but found {:?}",
3936                            index, value
3937                        )
3938                        .into_boxed_str(),
3939                    )),
3940                }
3941            }
3942            MaybeDetached::Attached(_) => {
3943                let Some(value) = self.get_(index) else {
3944                    return Err(LoroError::OutOfBound {
3945                        pos: index,
3946                        info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3947                        len: self.len(),
3948                    });
3949                };
3950                match value {
3951                    ValueOrHandler::Handler(handler) => Ok(handler),
3952                    ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
3953                        format!(
3954                            "Expected container at index {}, but found {:?}",
3955                            index, value
3956                        )
3957                        .into_boxed_str(),
3958                    )),
3959                }
3960            }
3961        }
3962    }
3963
3964    pub fn len(&self) -> usize {
3965        match &self.inner {
3966            MaybeDetached::Detached(d) => {
3967                let d = d.lock();
3968                d.value.len()
3969            }
3970            MaybeDetached::Attached(a) => {
3971                a.with_doc_state(|state| state.get_list_len(a.container_idx))
3972            }
3973        }
3974    }
3975
3976    pub fn is_empty(&self) -> bool {
3977        self.len() == 0
3978    }
3979
3980    pub fn get_deep_value_with_id(&self) -> LoroValue {
3981        let inner = self.inner.try_attached_state().unwrap();
3982        inner
3983            .doc
3984            .state
3985            .lock()
3986            .get_container_deep_value_with_id(inner.container_idx, None)
3987    }
3988
3989    pub fn get(&self, index: usize) -> Option<LoroValue> {
3990        match &self.inner {
3991            MaybeDetached::Detached(d) => {
3992                let d = d.lock();
3993                d.value.get(index).map(|v| v.to_value())
3994            }
3995            MaybeDetached::Attached(a) => {
3996                a.with_doc_state(|state| state.get_list_value_at(a.container_idx, index))
3997            }
3998        }
3999    }
4000
4001    /// Get value at given index, if it's a container, return a handler to the container
4002    pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
4003        match &self.inner {
4004            MaybeDetached::Detached(d) => {
4005                let d = d.lock();
4006                d.value.get(index).cloned()
4007            }
4008            MaybeDetached::Attached(m) => {
4009                let value =
4010                    m.with_doc_state(|state| state.get_list_value_at(m.container_idx, index));
4011                value.map(|value| value_to_value_or_handler(m, value))
4012            }
4013        }
4014    }
4015
4016    pub fn for_each<I>(&self, mut f: I)
4017    where
4018        I: FnMut(ValueOrHandler),
4019    {
4020        match &self.inner {
4021            MaybeDetached::Detached(d) => {
4022                let d = d.lock();
4023                for v in d.value.iter() {
4024                    f(v.clone());
4025                }
4026            }
4027            MaybeDetached::Attached(m) => {
4028                let temp = m.with_doc_state(|state| {
4029                    state
4030                        .get_list_values(m.container_idx)
4031                        .into_iter()
4032                        .map(|value| value_to_value_or_handler(m, value))
4033                        .collect::<Vec<_>>()
4034                });
4035
4036                for v in temp.into_iter() {
4037                    f(v);
4038                }
4039            }
4040        }
4041    }
4042
4043    pub fn log_internal_state(&self) -> String {
4044        match &self.inner {
4045            MaybeDetached::Detached(d) => {
4046                let d = d.lock();
4047                format!("{:#?}", &d.value)
4048            }
4049            MaybeDetached::Attached(a) => a.with_state(|state| {
4050                let a = state.as_movable_list_state().unwrap();
4051                format!("{a:#?}")
4052            }),
4053        }
4054    }
4055
4056    pub fn new_detached() -> MovableListHandler {
4057        MovableListHandler {
4058            inner: MaybeDetached::new_detached(Default::default()),
4059        }
4060    }
4061
4062    pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
4063        match &self.inner {
4064            MaybeDetached::Detached(_) => None,
4065            MaybeDetached::Attached(inner) => {
4066                let (id, len) = inner.with_state(|s| {
4067                    let l = s.as_movable_list_state().unwrap();
4068                    (l.get_list_item_id_at(pos), l.len())
4069                });
4070
4071                if len == 0 {
4072                    return Some(Cursor {
4073                        id: None,
4074                        container: self.id(),
4075                        side: if side == Side::Middle {
4076                            Side::Left
4077                        } else {
4078                            side
4079                        },
4080                        origin_pos: 0,
4081                    });
4082                }
4083
4084                if len <= pos {
4085                    return Some(Cursor {
4086                        id: None,
4087                        container: self.id(),
4088                        side: Side::Right,
4089                        origin_pos: len,
4090                    });
4091                }
4092
4093                let id = id?;
4094                Some(Cursor {
4095                    id: Some(id.id()),
4096                    container: self.id(),
4097                    side,
4098                    origin_pos: pos,
4099                })
4100            }
4101        }
4102    }
4103
4104    pub(crate) fn op_pos_to_user_pos(&self, new_pos: usize) -> usize {
4105        match &self.inner {
4106            MaybeDetached::Detached(_) => new_pos,
4107            MaybeDetached::Attached(inner) => {
4108                let mut pos = new_pos;
4109                inner.with_state(|s| {
4110                    let l = s.as_movable_list_state().unwrap();
4111                    pos = l
4112                        .convert_index(new_pos, IndexType::ForOp, IndexType::ForUser)
4113                        .unwrap_or(l.len());
4114                });
4115                pos
4116            }
4117        }
4118    }
4119
4120    pub fn is_deleted(&self) -> bool {
4121        match &self.inner {
4122            MaybeDetached::Detached(_) => false,
4123            MaybeDetached::Attached(a) => a.is_deleted(),
4124        }
4125    }
4126
4127    pub fn clear(&self) -> LoroResult<()> {
4128        match &self.inner {
4129            MaybeDetached::Detached(d) => {
4130                let mut d = d.lock();
4131                d.value.clear();
4132                Ok(())
4133            }
4134            MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4135        }
4136    }
4137
4138    pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4139        self.delete_with_txn(txn, 0, self.len())
4140    }
4141
4142    pub fn get_creator_at(&self, pos: usize) -> Option<PeerID> {
4143        match &self.inner {
4144            MaybeDetached::Detached(_) => None,
4145            MaybeDetached::Attached(a) => {
4146                a.with_state(|state| state.as_movable_list_state().unwrap().get_creator_at(pos))
4147            }
4148        }
4149    }
4150
4151    pub fn get_last_mover_at(&self, pos: usize) -> Option<PeerID> {
4152        match &self.inner {
4153            MaybeDetached::Detached(_) => None,
4154            MaybeDetached::Attached(a) => a.with_state(|state| {
4155                state
4156                    .as_movable_list_state()
4157                    .unwrap()
4158                    .get_last_mover_at(pos)
4159            }),
4160        }
4161    }
4162
4163    pub fn get_last_editor_at(&self, pos: usize) -> Option<PeerID> {
4164        match &self.inner {
4165            MaybeDetached::Detached(_) => None,
4166            MaybeDetached::Attached(a) => a.with_state(|state| {
4167                state
4168                    .as_movable_list_state()
4169                    .unwrap()
4170                    .get_last_editor_at(pos)
4171            }),
4172        }
4173    }
4174}
4175
4176impl MapHandler {
4177    /// Create a new container that is detached from the document.
4178    /// The edits on a detached container will not be persisted.
4179    /// To attach the container to the document, please insert it into an attached container.
4180    pub fn new_detached() -> Self {
4181        Self {
4182            inner: MaybeDetached::new_detached(Default::default()),
4183        }
4184    }
4185
4186    pub fn insert(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
4187        match &self.inner {
4188            MaybeDetached::Detached(m) => {
4189                let mut m = m.lock();
4190                let value = value.into();
4191                ensure_no_regular_container_value(&value)?;
4192                m.value.insert(key.into(), ValueOrHandler::Value(value));
4193                Ok(())
4194            }
4195            MaybeDetached::Attached(a) => {
4196                a.with_txn(|txn| self.insert_with_txn(txn, key, value.into()))
4197            }
4198        }
4199    }
4200
4201    /// This method will insert the value even if the same value is already in the given entry.
4202    fn insert_without_skipping(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
4203        match &self.inner {
4204            MaybeDetached::Detached(m) => {
4205                let mut m = m.lock();
4206                let value = value.into();
4207                ensure_no_regular_container_value(&value)?;
4208                m.value.insert(key.into(), ValueOrHandler::Value(value));
4209                Ok(())
4210            }
4211            MaybeDetached::Attached(a) => a.with_txn(|txn| {
4212                let this = &self;
4213                let value = value.into();
4214                ensure_no_regular_container_value(&value)?;
4215
4216                let inner = this.inner.try_attached_state()?;
4217                txn.apply_local_op(
4218                    inner.container_idx,
4219                    crate::op::RawOpContent::Map(crate::container::map::MapSet {
4220                        key: key.into(),
4221                        value: Some(value.clone()),
4222                    }),
4223                    EventHint::Map {
4224                        key: key.into(),
4225                        value: Some(value.clone()),
4226                    },
4227                    &inner.doc,
4228                )
4229            }),
4230        }
4231    }
4232
4233    pub fn insert_with_txn(
4234        &self,
4235        txn: &mut Transaction,
4236        key: &str,
4237        value: LoroValue,
4238    ) -> LoroResult<()> {
4239        ensure_no_regular_container_value(&value)?;
4240
4241        if self.get(key).map(|x| x == value).unwrap_or(false) {
4242            // skip if the value is already set
4243            return Ok(());
4244        }
4245
4246        let inner = self.inner.try_attached_state()?;
4247        txn.apply_local_op(
4248            inner.container_idx,
4249            crate::op::RawOpContent::Map(crate::container::map::MapSet {
4250                key: key.into(),
4251                value: Some(value.clone()),
4252            }),
4253            EventHint::Map {
4254                key: key.into(),
4255                value: Some(value.clone()),
4256            },
4257            &inner.doc,
4258        )
4259    }
4260
4261    pub fn insert_container<T: HandlerTrait>(&self, key: &str, handler: T) -> LoroResult<T> {
4262        match &self.inner {
4263            MaybeDetached::Detached(m) => {
4264                let mut m = m.lock();
4265                let to_insert = handler.to_handler();
4266                m.value
4267                    .insert(key.into(), ValueOrHandler::Handler(to_insert.clone()));
4268                Ok(handler)
4269            }
4270            MaybeDetached::Attached(a) => {
4271                a.with_txn(|txn| self.insert_container_with_txn(txn, key, handler))
4272            }
4273        }
4274    }
4275
4276    pub fn insert_container_with_txn<H: HandlerTrait>(
4277        &self,
4278        txn: &mut Transaction,
4279        key: &str,
4280        child: H,
4281    ) -> LoroResult<H> {
4282        let inner = self.inner.try_attached_state()?;
4283        let id = txn.next_id();
4284        let container_id = ContainerID::new_normal(id, child.kind());
4285        txn.apply_local_op(
4286            inner.container_idx,
4287            crate::op::RawOpContent::Map(crate::container::map::MapSet {
4288                key: key.into(),
4289                value: Some(LoroValue::Container(container_id.clone())),
4290            }),
4291            EventHint::Map {
4292                key: key.into(),
4293                value: Some(LoroValue::Container(container_id.clone())),
4294            },
4295            &inner.doc,
4296        )?;
4297
4298        child.attach(txn, inner, container_id)
4299    }
4300
4301    pub fn delete(&self, key: &str) -> LoroResult<()> {
4302        match &self.inner {
4303            MaybeDetached::Detached(m) => {
4304                let mut m = m.lock();
4305                m.value.remove(key);
4306                Ok(())
4307            }
4308            MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, key)),
4309        }
4310    }
4311
4312    pub fn delete_with_txn(&self, txn: &mut Transaction, key: &str) -> LoroResult<()> {
4313        let inner = self.inner.try_attached_state()?;
4314        txn.apply_local_op(
4315            inner.container_idx,
4316            crate::op::RawOpContent::Map(crate::container::map::MapSet {
4317                key: key.into(),
4318                value: None,
4319            }),
4320            EventHint::Map {
4321                key: key.into(),
4322                value: None,
4323            },
4324            &inner.doc,
4325        )
4326    }
4327
4328    pub fn for_each<I>(&self, mut f: I)
4329    where
4330        I: FnMut(&str, ValueOrHandler),
4331    {
4332        match &self.inner {
4333            MaybeDetached::Detached(m) => {
4334                let m = m.lock();
4335                for (k, v) in m.value.iter() {
4336                    f(k, v.clone());
4337                }
4338            }
4339            MaybeDetached::Attached(inner) => {
4340                let temp = inner.with_doc_state(|state| {
4341                    state
4342                        .get_map_entries(inner.container_idx)
4343                        .into_iter()
4344                        .map(|(key, value)| {
4345                            let translated = loro_common::translate_mergeable_marker_value(
4346                                &inner.id,
4347                                key.as_ref(),
4348                                value,
4349                            );
4350                            (
4351                                key.to_string(),
4352                                value_to_value_or_handler(inner, translated),
4353                            )
4354                        })
4355                        .collect::<Vec<_>>()
4356                });
4357
4358                for (k, v) in temp.into_iter() {
4359                    f(&k, v.clone());
4360                }
4361            }
4362        }
4363    }
4364
4365    pub fn get_child_handler(&self, key: &str) -> LoroResult<Handler> {
4366        match &self.inner {
4367            MaybeDetached::Detached(m) => {
4368                let m = m.lock();
4369                let value = m.value.get(key).unwrap();
4370                match value {
4371                    ValueOrHandler::Value(v) => Err(LoroError::ArgErr(
4372                        format!("Expected Handler but found {:?}", v).into_boxed_str(),
4373                    )),
4374                    ValueOrHandler::Handler(h) => Ok(h.clone()),
4375                }
4376            }
4377            MaybeDetached::Attached(_) => {
4378                let Some(value) = self.get_(key) else {
4379                    return Err(LoroError::ArgErr(
4380                        format!("Key {key} does not exist").into_boxed_str(),
4381                    ));
4382                };
4383                match value {
4384                    ValueOrHandler::Handler(handler) => Ok(handler),
4385                    ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
4386                        format!("Expected Handler but found {:?}", value).into_boxed_str(),
4387                    )),
4388                }
4389            }
4390        }
4391    }
4392
4393    pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
4394        match &self.inner {
4395            MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
4396                method: "get_deep_value_with_id",
4397            }),
4398            MaybeDetached::Attached(inner) => Ok(inner.with_doc_state(|state| {
4399                state.get_container_deep_value_with_id(inner.container_idx, None)
4400            })),
4401        }
4402    }
4403
4404    pub fn get(&self, key: &str) -> Option<LoroValue> {
4405        match &self.inner {
4406            MaybeDetached::Detached(m) => {
4407                let m = m.lock();
4408                m.value.get(key).map(|v| v.to_value())
4409            }
4410            MaybeDetached::Attached(inner) => {
4411                let value = inner
4412                    .with_doc_state(|state| state.get_map_value_by_key(inner.container_idx, key))?;
4413                Some(loro_common::translate_mergeable_marker_value(
4414                    &inner.id, key, value,
4415                ))
4416            }
4417        }
4418    }
4419
4420    /// Get the value at given key, if value is a container, return a handler to the container
4421    pub fn get_(&self, key: &str) -> Option<ValueOrHandler> {
4422        match &self.inner {
4423            MaybeDetached::Detached(m) => {
4424                let m = m.lock();
4425                m.value.get(key).cloned()
4426            }
4427            MaybeDetached::Attached(inner) => {
4428                let value = inner
4429                    .with_doc_state(|state| state.get_map_value_by_key(inner.container_idx, key))?;
4430                let value = loro_common::translate_mergeable_marker_value(&inner.id, key, value);
4431                Some(value_to_value_or_handler(inner, value))
4432            }
4433        }
4434    }
4435
4436    /// Get or create a regular child container at `key`.
4437    ///
4438    /// This legacy method creates regular op-id child containers when the key is empty or `null`.
4439    /// It is not mergeable: concurrent first creation at the same map key can fork child state and
4440    /// leave one branch hidden by map conflict resolution. Prefer `ensure_mergeable_*` for lazy
4441    /// map-key child creation.
4442    #[deprecated(
4443        note = "use ensure_mergeable_map/list/movable_list/text/tree/counter for lazy map-key child creation; this method creates regular op-id children"
4444    )]
4445    pub fn get_or_create_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4446        if let Some(ans) = self.get_(key) {
4447            if let ValueOrHandler::Handler(h) = ans {
4448                let kind = h.kind();
4449                return C::from_handler(h).ok_or_else(move || {
4450                    LoroError::ArgErr(
4451                        format!("Expected value type {} but found {:?}", child.kind(), kind)
4452                            .into_boxed_str(),
4453                    )
4454                });
4455            } else if let ValueOrHandler::Value(LoroValue::Null) = ans {
4456                // do nothing
4457            } else {
4458                return Err(LoroError::ArgErr(
4459                    format!("Expected value type {} but found {:?}", child.kind(), ans)
4460                        .into_boxed_str(),
4461                ));
4462            }
4463        }
4464
4465        self.insert_container(key, child)
4466    }
4467
4468    /// Shared implementation for all `ensure_mergeable_*` methods.
4469    ///
4470    /// Computes a deterministic [`ContainerID::Root`] in the mergeable namespace from
4471    /// `(parent.id, key, child.kind())` and constructs the handler from it. Two peers calling this
4472    /// with the same `(parent, key, kind)` receive handlers with identical container ids, which is
4473    /// what makes the child container mergeable on concurrent first-write.
4474    ///
4475    /// # Errors
4476    ///
4477    /// Returns [`LoroError::MisuseDetachedContainer`] when called on a detached handler. The
4478    /// deterministic cid is computed from the parent's cid, which a detached parent does not have
4479    /// yet; falling back to a non-deterministic regular child would silently drop the mergeable
4480    /// guarantee at attach time. Detached callers must attach the parent first.
4481    ///
4482    /// Returns [`LoroError::ArgErr`] if the parent slot already holds a non-mergeable value, or if
4483    /// `C::from_handler` rejects the handler built from the deterministic cid (unreachable by
4484    /// construction; guards against future drift between `from_handler` and `kind`).
4485    fn ensure_mergeable_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4486        let MaybeDetached::Attached(parent) = &self.inner else {
4487            return Err(LoroError::MisuseDetachedContainer {
4488                method: "ensure_mergeable_container",
4489            });
4490        };
4491
4492        // Compare against the raw marker bytes (skipping `MapHandler::get`'s user-facing
4493        // marker → Container translation) so the non-mergeable-occupant guard sees the real
4494        // slot value and the same-kind idempotent-skip can match.
4495        let existing_raw =
4496            parent.with_doc_state(|state| state.get_map_value_by_key(parent.container_idx, key));
4497
4498        // A non-mergeable occupant (scalar, arbitrary binary, regular child container) would be
4499        // silently clobbered by the marker write, so reject rather than overwrite under a
4500        // `get_`-named API. Only the exact binary marker for this `(parent, key, kind)` is
4501        // accepted as an existing mergeable occupant.
4502        if let Some(existing) = &existing_raw {
4503            if !matches!(existing, LoroValue::Null)
4504                && loro_common::parse_mergeable_marker(&parent.id, key, existing).is_none()
4505            {
4506                return Err(LoroError::ArgErr(
4507                    format!(
4508                        "Cannot create a mergeable {} at key {key:?}: the key already holds a non-mergeable value",
4509                        child.kind()
4510                    )
4511                    .into_boxed_str(),
4512                ));
4513            }
4514        }
4515
4516        let cid = ContainerID::new_mergeable(&parent.id, key, child.kind());
4517        let marker = loro_common::mergeable_marker(&parent.id, key, child.kind());
4518
4519        // Idempotent-skip on same marker: `MapHandler::get` translates markers to Container, so
4520        // `insert_with_txn`'s equality check can't see this collision — do it directly.
4521        // A different-kind marker is a deliberate kind change; let the insert through.
4522        if existing_raw.as_ref() != Some(&marker) {
4523            self.insert(key, marker)?;
4524        }
4525
4526        C::from_handler(create_handler(parent, cid.clone())).ok_or_else(|| {
4527            LoroError::ArgErr(
4528                format!(
4529                    "Expected value type {} but found {}",
4530                    child.kind(),
4531                    cid.container_type()
4532                )
4533                .into_boxed_str(),
4534            )
4535        })
4536    }
4537
4538    #[cfg(feature = "counter")]
4539    /// Ensure a mergeable Counter child exists under `key` and return its handler.
4540    ///
4541    /// Returns [`LoroError::MisuseDetachedContainer`] when called on a detached map.
4542    /// Returns [`LoroError::ArgErr`] if the parent slot already holds a non-mergeable value.
4543    /// Repeated same-kind calls are idempotent; different mergeable kinds deliberately rewrite
4544    /// the active marker while preserving each mergeable child's deterministic state.
4545    pub fn ensure_mergeable_counter(&self, key: &str) -> LoroResult<counter::CounterHandler> {
4546        self.ensure_mergeable_container(key, counter::CounterHandler::new_detached())
4547    }
4548
4549    /// Ensure a mergeable Map child exists under `key` and return its handler.
4550    ///
4551    /// Returns [`LoroError::MisuseDetachedContainer`] when called on a detached map.
4552    /// Returns [`LoroError::ArgErr`] if the parent slot already holds a non-mergeable value.
4553    /// Repeated same-kind calls are idempotent; different mergeable kinds deliberately rewrite
4554    /// the active marker while preserving each mergeable child's deterministic state.
4555    ///
4556    /// Prefer to avoid very deep mergeable-map chains: mergeable cids encode their flattened
4557    /// logical path, so cid size still grows with depth and rides through every op/snapshot
4558    /// reference to it. See [`MERGEABLE_NAMESPACE_PREFIX`](loro_common::MERGEABLE_NAMESPACE_PREFIX).
4559    pub fn ensure_mergeable_map(&self, key: &str) -> LoroResult<MapHandler> {
4560        self.ensure_mergeable_container(key, MapHandler::new_detached())
4561    }
4562
4563    /// Ensure a mergeable List child exists under `key` and return its handler.
4564    ///
4565    /// Returns [`LoroError::MisuseDetachedContainer`] when called on a detached map.
4566    /// Returns [`LoroError::ArgErr`] if the parent slot already holds a non-mergeable value.
4567    /// Repeated same-kind calls are idempotent; different mergeable kinds deliberately rewrite
4568    /// the active marker while preserving each mergeable child's deterministic state.
4569    pub fn ensure_mergeable_list(&self, key: &str) -> LoroResult<ListHandler> {
4570        self.ensure_mergeable_container(key, ListHandler::new_detached())
4571    }
4572
4573    /// Ensure a mergeable MovableList child exists under `key` and return its handler.
4574    ///
4575    /// Returns [`LoroError::MisuseDetachedContainer`] when called on a detached map.
4576    /// Returns [`LoroError::ArgErr`] if the parent slot already holds a non-mergeable value.
4577    /// Repeated same-kind calls are idempotent; different mergeable kinds deliberately rewrite
4578    /// the active marker while preserving each mergeable child's deterministic state.
4579    pub fn ensure_mergeable_movable_list(&self, key: &str) -> LoroResult<MovableListHandler> {
4580        self.ensure_mergeable_container(key, MovableListHandler::new_detached())
4581    }
4582
4583    /// Ensure a mergeable Text child exists under `key` and return its handler.
4584    ///
4585    /// Returns [`LoroError::MisuseDetachedContainer`] when called on a detached map.
4586    /// Returns [`LoroError::ArgErr`] if the parent slot already holds a non-mergeable value.
4587    /// Repeated same-kind calls are idempotent; different mergeable kinds deliberately rewrite
4588    /// the active marker while preserving each mergeable child's deterministic state.
4589    pub fn ensure_mergeable_text(&self, key: &str) -> LoroResult<TextHandler> {
4590        self.ensure_mergeable_container(key, TextHandler::new_detached())
4591    }
4592
4593    /// Ensure a mergeable Tree child exists under `key` and return its handler.
4594    ///
4595    /// Returns [`LoroError::MisuseDetachedContainer`] when called on a detached map.
4596    /// Returns [`LoroError::ArgErr`] if the parent slot already holds a non-mergeable value.
4597    /// Repeated same-kind calls are idempotent; different mergeable kinds deliberately rewrite
4598    /// the active marker while preserving each mergeable child's deterministic state.
4599    pub fn ensure_mergeable_tree(&self, key: &str) -> LoroResult<TreeHandler> {
4600        self.ensure_mergeable_container(key, TreeHandler::new_detached())
4601    }
4602
4603    pub fn contains_key(&self, key: &str) -> bool {
4604        self.get(key).is_some()
4605    }
4606
4607    pub fn len(&self) -> usize {
4608        match &self.inner {
4609            MaybeDetached::Detached(m) => m.lock().value.len(),
4610            MaybeDetached::Attached(a) => {
4611                a.with_doc_state(|state| state.get_map_len(a.container_idx))
4612            }
4613        }
4614    }
4615
4616    pub fn is_empty(&self) -> bool {
4617        self.len() == 0
4618    }
4619
4620    pub fn is_deleted(&self) -> bool {
4621        match &self.inner {
4622            MaybeDetached::Detached(_) => false,
4623            MaybeDetached::Attached(a) => a.is_deleted(),
4624        }
4625    }
4626
4627    pub fn clear(&self) -> LoroResult<()> {
4628        match &self.inner {
4629            MaybeDetached::Detached(m) => {
4630                let mut m = m.lock();
4631                m.value.clear();
4632                Ok(())
4633            }
4634            MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4635        }
4636    }
4637
4638    pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4639        let keys: Vec<InternalString> = self.inner.try_attached_state()?.with_state(|state| {
4640            state
4641                .as_map_state()
4642                .unwrap()
4643                .iter()
4644                .map(|(k, _)| k.clone())
4645                .collect()
4646        });
4647
4648        for key in keys {
4649            self.delete_with_txn(txn, &key)?;
4650        }
4651
4652        Ok(())
4653    }
4654
4655    pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
4656        let keys: Vec<InternalString> = match &self.inner {
4657            MaybeDetached::Detached(m) => {
4658                let m = m.lock();
4659                m.value.keys().map(|x| x.as_str().into()).collect()
4660            }
4661            MaybeDetached::Attached(a) => {
4662                a.with_doc_state(|state| state.get_map_keys(a.container_idx))
4663            }
4664        };
4665
4666        keys.into_iter()
4667    }
4668
4669    pub fn values(&self) -> impl Iterator<Item = ValueOrHandler> + '_ {
4670        let values: Vec<ValueOrHandler> = match &self.inner {
4671            MaybeDetached::Detached(m) => {
4672                let m = m.lock();
4673                m.value.values().cloned().collect()
4674            }
4675            MaybeDetached::Attached(a) => a.with_doc_state(|state| {
4676                // A mergeable child's marker lives in the parent map's value table; iterate
4677                // entries (key + value) so the user-boundary translation can resolve each marker
4678                // to the deterministic child cid before wrapping into a handler.
4679                state
4680                    .get_map_entries(a.container_idx)
4681                    .into_iter()
4682                    .map(|(key, value)| {
4683                        let translated = loro_common::translate_mergeable_marker_value(
4684                            &a.id,
4685                            key.as_ref(),
4686                            value,
4687                        );
4688                        value_to_value_or_handler(a, translated)
4689                    })
4690                    .collect()
4691            }),
4692        };
4693
4694        values.into_iter()
4695    }
4696
4697    pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
4698        match &self.inner {
4699            MaybeDetached::Detached(_) => None,
4700            MaybeDetached::Attached(a) => a.with_state(|state| {
4701                let m = state.as_map_state().unwrap();
4702                m.get_last_edit_peer(key)
4703            }),
4704        }
4705    }
4706}
4707
4708fn with_txn<R>(doc: &LoroDoc, f: impl FnOnce(&mut Transaction) -> LoroResult<R>) -> LoroResult<R> {
4709    let txn = &doc.txn;
4710    let mut txn = txn.lock();
4711    loop {
4712        if let Some(txn) = &mut *txn {
4713            return f(txn);
4714        } else if cfg!(target_arch = "wasm32") || !doc.can_edit() {
4715            return Err(LoroError::AutoCommitNotStarted);
4716        } else {
4717            drop(txn);
4718            #[cfg(loom)]
4719            loom::thread::yield_now();
4720            doc.start_auto_commit();
4721            txn = doc.txn.lock();
4722        }
4723    }
4724}
4725
4726#[cfg(feature = "counter")]
4727pub mod counter {
4728
4729    use loro_common::LoroResult;
4730
4731    use crate::{
4732        txn::{EventHint, Transaction},
4733        HandlerTrait,
4734    };
4735
4736    use super::{create_handler, Handler, MaybeDetached};
4737
4738    #[derive(Clone)]
4739    pub struct CounterHandler {
4740        pub(super) inner: MaybeDetached<f64>,
4741    }
4742
4743    impl CounterHandler {
4744        pub fn new_detached() -> Self {
4745            Self {
4746                inner: MaybeDetached::new_detached(0.),
4747            }
4748        }
4749
4750        pub fn increment(&self, n: f64) -> LoroResult<()> {
4751            match &self.inner {
4752                MaybeDetached::Detached(d) => {
4753                    let d = &mut d.lock().value;
4754                    *d += n;
4755                    Ok(())
4756                }
4757                MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, n)),
4758            }
4759        }
4760
4761        pub fn decrement(&self, n: f64) -> LoroResult<()> {
4762            match &self.inner {
4763                MaybeDetached::Detached(d) => {
4764                    let d = &mut d.lock().value;
4765                    *d -= n;
4766                    Ok(())
4767                }
4768                MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, -n)),
4769            }
4770        }
4771
4772        fn increment_with_txn(&self, txn: &mut Transaction, n: f64) -> LoroResult<()> {
4773            let inner = self.inner.try_attached_state()?;
4774            txn.apply_local_op(
4775                inner.container_idx,
4776                crate::op::RawOpContent::Counter(n),
4777                EventHint::Counter(n),
4778                &inner.doc,
4779            )
4780        }
4781
4782        pub fn is_deleted(&self) -> bool {
4783            match &self.inner {
4784                MaybeDetached::Detached(_) => false,
4785                MaybeDetached::Attached(a) => a.is_deleted(),
4786            }
4787        }
4788
4789        pub fn clear(&self) -> LoroResult<()> {
4790            self.decrement(self.get_value().into_double().unwrap())
4791        }
4792    }
4793
4794    impl std::fmt::Debug for CounterHandler {
4795        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4796            match &self.inner {
4797                MaybeDetached::Detached(_) => write!(f, "CounterHandler Detached"),
4798                MaybeDetached::Attached(a) => write!(f, "CounterHandler {}", a.id),
4799            }
4800        }
4801    }
4802
4803    impl HandlerTrait for CounterHandler {
4804        fn is_attached(&self) -> bool {
4805            matches!(&self.inner, MaybeDetached::Attached(..))
4806        }
4807
4808        fn attached_handler(&self) -> Option<&crate::BasicHandler> {
4809            self.inner.attached_handler()
4810        }
4811
4812        fn get_value(&self) -> loro_common::LoroValue {
4813            match &self.inner {
4814                MaybeDetached::Detached(t) => {
4815                    let t = t.lock();
4816                    t.value.into()
4817                }
4818                MaybeDetached::Attached(a) => a.get_value(),
4819            }
4820        }
4821
4822        fn get_deep_value(&self) -> loro_common::LoroValue {
4823            self.get_value()
4824        }
4825
4826        fn kind(&self) -> loro_common::ContainerType {
4827            loro_common::ContainerType::Counter
4828        }
4829
4830        fn to_handler(&self) -> super::Handler {
4831            Handler::Counter(self.clone())
4832        }
4833
4834        fn from_handler(h: super::Handler) -> Option<Self> {
4835            match h {
4836                Handler::Counter(x) => Some(x),
4837                _ => None,
4838            }
4839        }
4840
4841        fn attach(
4842            &self,
4843            txn: &mut crate::txn::Transaction,
4844            parent: &crate::BasicHandler,
4845            self_id: loro_common::ContainerID,
4846        ) -> loro_common::LoroResult<Self> {
4847            match &self.inner {
4848                MaybeDetached::Detached(v) => {
4849                    let mut v = v.lock();
4850                    let inner = create_handler(parent, self_id);
4851                    let c = inner.into_counter().unwrap();
4852
4853                    c.increment_with_txn(txn, v.value)?;
4854
4855                    v.attached = c.attached_handler().cloned();
4856                    Ok(c)
4857                }
4858                MaybeDetached::Attached(a) => {
4859                    let new_inner = create_handler(a, self_id);
4860                    let ans = new_inner.into_counter().unwrap();
4861                    let delta = *self.get_value().as_double().unwrap();
4862                    ans.increment_with_txn(txn, delta)?;
4863                    Ok(ans)
4864                }
4865            }
4866        }
4867
4868        fn get_attached(&self) -> Option<Self> {
4869            match &self.inner {
4870                MaybeDetached::Attached(a) => Some(Self {
4871                    inner: MaybeDetached::Attached(a.clone()),
4872                }),
4873                MaybeDetached::Detached(v) => v.lock().attached.clone().map(|x| Self {
4874                    inner: MaybeDetached::Attached(x),
4875                }),
4876            }
4877        }
4878
4879        fn doc(&self) -> Option<crate::LoroDoc> {
4880            match &self.inner {
4881                MaybeDetached::Detached(_) => None,
4882                MaybeDetached::Attached(a) => Some(a.doc()),
4883            }
4884        }
4885    }
4886}
4887
4888#[cfg(test)]
4889mod test {
4890    use std::borrow::Cow;
4891
4892    use super::{
4893        Handler, HandlerTrait, ListHandler, MapHandler, MovableListHandler, TextDelta, TextHandler,
4894        ValueOrHandler,
4895    };
4896    use crate::container::list::list_op::ListOp;
4897    use crate::cursor::PosType;
4898    use crate::loro::ExportMode;
4899    use crate::op::ListSlice;
4900    use crate::state::TreeParentId;
4901    use crate::txn::EventHint;
4902    use crate::version::Frontiers;
4903    use crate::LoroDoc;
4904    use crate::{fx_map, ToJson};
4905    use loro_common::{ContainerID, ContainerType, LoroError, LoroValue, ID};
4906    use serde_json::json;
4907
4908    fn recheck_fast_blob(mut bytes: Vec<u8>) -> Vec<u8> {
4909        let checksum = xxhash_rust::xxh32::xxh32(&bytes[20..], u32::from_le_bytes(*b"LORO"));
4910        bytes[16..20].copy_from_slice(&checksum.to_le_bytes());
4911        bytes
4912    }
4913
4914    fn replace_fast_snapshot_state_bytes(mut snapshot: Vec<u8>, state_bytes: &[u8]) -> Vec<u8> {
4915        let mut body = &snapshot[22..];
4916        let oplog_len = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
4917        body = &body[4 + oplog_len..];
4918        let old_state_len = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
4919        let state_len_pos = 22 + 4 + oplog_len;
4920        let state_start = state_len_pos + 4;
4921        let state_end = state_start + old_state_len;
4922        snapshot[state_len_pos..state_start]
4923            .copy_from_slice(&(state_bytes.len() as u32).to_le_bytes());
4924        snapshot.splice(state_start..state_end, state_bytes.iter().copied());
4925        recheck_fast_blob(snapshot)
4926    }
4927
4928    fn insert_many_with_single_list_op(
4929        txn: &mut crate::txn::Transaction,
4930        list: &crate::handler::ListHandler,
4931        pos: usize,
4932        values: Vec<LoroValue>,
4933    ) {
4934        let len = values.len();
4935        let inner = list.inner.try_attached_state().unwrap();
4936        txn.apply_local_op(
4937            inner.container_idx,
4938            crate::op::RawOpContent::List(ListOp::Insert {
4939                slice: ListSlice::RawData(Cow::Owned(values)),
4940                pos,
4941            }),
4942            EventHint::InsertList {
4943                len: len as u32,
4944                pos,
4945            },
4946            &inner.doc,
4947        )
4948        .unwrap();
4949    }
4950
4951    #[test]
4952    fn richtext_handler() {
4953        let loro = LoroDoc::new();
4954        loro.set_peer_id(1).unwrap();
4955        let loro2 = LoroDoc::new();
4956        loro2.set_peer_id(2).unwrap();
4957
4958        let mut txn = loro.txn().unwrap();
4959        let text = txn.get_text("hello");
4960        text.insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
4961            .unwrap();
4962        txn.commit().unwrap();
4963        let exported = loro.export(ExportMode::all_updates()).unwrap();
4964
4965        loro2.import(&exported).unwrap();
4966        let mut txn = loro2.txn().unwrap();
4967        let text = txn.get_text("hello");
4968        assert_eq!(&**text.get_value().as_string().unwrap(), "hello");
4969        text.insert_with_txn(&mut txn, 5, " world", PosType::Unicode)
4970            .unwrap();
4971        assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
4972        txn.commit().unwrap();
4973
4974        loro.import(&loro2.export(ExportMode::all_updates()).unwrap())
4975            .unwrap();
4976        let txn = loro.txn().unwrap();
4977        let text = txn.get_text("hello");
4978        assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
4979        txn.commit().unwrap();
4980
4981        // test checkout
4982        loro.checkout(&Frontiers::from_id(ID::new(2, 1))).unwrap();
4983        assert_eq!(&**text.get_value().as_string().unwrap(), "hello w");
4984    }
4985
4986    #[test]
4987    fn richtext_handler_concurrent() {
4988        let loro = LoroDoc::new();
4989        let mut txn = loro.txn().unwrap();
4990        let handler = loro.get_text("richtext");
4991        handler
4992            .insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
4993            .unwrap();
4994        txn.commit().unwrap();
4995        for i in 0..100 {
4996            let new_loro = LoroDoc::new();
4997            new_loro
4998                .import(&loro.export(ExportMode::all_updates()).unwrap())
4999                .unwrap();
5000            let mut txn = new_loro.txn().unwrap();
5001            let handler = new_loro.get_text("richtext");
5002            handler
5003                .insert_with_txn(&mut txn, i % 5, &i.to_string(), PosType::Unicode)
5004                .unwrap();
5005            txn.commit().unwrap();
5006            loro.import(
5007                &new_loro
5008                    .export(ExportMode::updates(&loro.oplog_vv()))
5009                    .unwrap(),
5010            )
5011            .unwrap();
5012        }
5013    }
5014
5015    #[test]
5016    fn list_import_batch_stays_consistent_after_repeated_tail_splits() {
5017        let doc_a = LoroDoc::new();
5018        doc_a.set_peer_id(1).unwrap();
5019        let mut txn = doc_a.txn().unwrap();
5020        let list_a = txn.get_list("list");
5021        insert_many_with_single_list_op(
5022            &mut txn,
5023            &list_a,
5024            0,
5025            (0..300).map(|i| LoroValue::I64(i)).collect(),
5026        );
5027        txn.commit().unwrap();
5028
5029        let doc_b = LoroDoc::new();
5030        doc_b.set_peer_id(2).unwrap();
5031        doc_b
5032            .import(&doc_a.export(ExportMode::all_updates()).unwrap())
5033            .unwrap();
5034
5035        let list_b = doc_b.get_list("list");
5036        let mut vv = doc_a.oplog_vv();
5037        let mut updates = Vec::new();
5038        for (i, pos) in [100, 201, 252, 278].into_iter().enumerate() {
5039            list_b.insert(pos, 1000 + i as i64).unwrap();
5040            updates.push(doc_b.export(ExportMode::updates(&vv)).unwrap());
5041            vv = doc_b.oplog_vv();
5042        }
5043
5044        doc_a.import_batch(&updates).unwrap();
5045        doc_a.check_state_diff_calc_consistency_slow();
5046        doc_b.check_state_diff_calc_consistency_slow();
5047        assert_eq!(doc_a.get_deep_value(), doc_b.get_deep_value());
5048    }
5049
5050    #[test]
5051    fn richtext_handler_mark() {
5052        let loro = LoroDoc::new_auto_commit();
5053        let handler = loro.get_text("richtext");
5054        handler.insert(0, "hello world", PosType::Unicode).unwrap();
5055        handler
5056            .mark(0, 5, "bold", true.into(), PosType::Event)
5057            .unwrap();
5058        loro.commit_then_renew();
5059
5060        // assert has bold
5061        let value = handler.get_richtext_value();
5062        assert_eq!(value[0]["insert"], "hello".into());
5063        let meta = value[0]["attributes"].as_map().unwrap();
5064        assert_eq!(meta.len(), 1);
5065        meta.get("bold").unwrap();
5066
5067        let loro2 = LoroDoc::new_auto_commit();
5068        loro2
5069            .import(&loro.export(ExportMode::all_updates()).unwrap())
5070            .unwrap();
5071        let handler2 = loro2.get_text("richtext");
5072        assert_eq!(&**handler2.get_value().as_string().unwrap(), "hello world");
5073
5074        // assert has bold
5075        let value = handler2.get_richtext_value();
5076        assert_eq!(value[0]["insert"], "hello".into());
5077        let meta = value[0]["attributes"].as_map().unwrap();
5078        assert_eq!(meta.len(), 1);
5079        meta.get("bold").unwrap();
5080
5081        // insert after bold should be bold
5082        {
5083            handler2.insert(5, " new", PosType::Unicode).unwrap();
5084            let value = handler2.get_richtext_value();
5085            assert_eq!(
5086                value.to_json_value(),
5087                serde_json::json!([
5088                    {"insert": "hello new", "attributes": {"bold": true}},
5089                    {"insert": " world"}
5090                ])
5091            );
5092        }
5093    }
5094
5095    #[test]
5096    fn richtext_snapshot() {
5097        let loro = LoroDoc::new();
5098        let mut txn = loro.txn().unwrap();
5099        let handler = loro.get_text("richtext");
5100        handler
5101            .insert_with_txn(&mut txn, 0, "hello world", PosType::Unicode)
5102            .unwrap();
5103        handler
5104            .mark_with_txn(&mut txn, 0, 5, "bold", true.into(), PosType::Event)
5105            .unwrap();
5106        txn.commit().unwrap();
5107
5108        let loro2 = LoroDoc::new();
5109        loro2
5110            .import(&loro.export(ExportMode::snapshot()).unwrap())
5111            .unwrap();
5112        let handler2 = loro2.get_text("richtext");
5113        assert_eq!(
5114            handler2.get_richtext_value().to_json_value(),
5115            serde_json::json!([
5116                {"insert": "hello", "attributes": {"bold": true}},
5117                {"insert": " world"}
5118            ])
5119        );
5120    }
5121
5122    #[test]
5123    fn text_snapshot_string_queries_do_not_decode_state() {
5124        let loro = LoroDoc::new_auto_commit();
5125        let text = loro.get_text("text");
5126        text.insert(0, "a😀文", PosType::Unicode).unwrap();
5127        text.mark(1, 3, "bold", true.into(), PosType::Unicode)
5128            .unwrap();
5129
5130        let restored = LoroDoc::new();
5131        restored
5132            .import(&loro.export(ExportMode::snapshot()).unwrap())
5133            .unwrap();
5134        let text = restored.get_text("text");
5135        assert!(!text.attached_handler().unwrap().has_decoded_state());
5136
5137        assert_eq!(text.len_unicode(), 3);
5138        assert_eq!(text.len_utf16(), 4);
5139        assert_eq!(text.len_utf8(), "a😀文".len());
5140        assert_eq!(text.char_at(1, PosType::Unicode).unwrap(), '😀');
5141        assert_eq!(text.slice(1, 3, PosType::Unicode).unwrap(), "😀文");
5142        assert_eq!(
5143            text.convert_pos(2, PosType::Unicode, PosType::Utf16),
5144            Some(3)
5145        );
5146        assert!(matches!(
5147            text.delete_utf16(2, 1),
5148            Err(LoroError::UTF16InUnicodeCodePoint { pos: 2 })
5149        ));
5150        assert!(matches!(
5151            text.delete_utf8(2, 1),
5152            Err(LoroError::UTF8InUnicodeCodePoint { pos: 2 })
5153        ));
5154        assert!(matches!(
5155            text.slice_delta(2, 3, PosType::Utf16),
5156            Err(LoroError::UTF16InUnicodeCodePoint { pos: 2 })
5157        ));
5158        assert!(matches!(
5159            text.slice_delta(2, 3, PosType::Bytes),
5160            Err(LoroError::UTF8InUnicodeCodePoint { pos: 2 })
5161        ));
5162        assert!(!text.attached_handler().unwrap().has_decoded_state());
5163
5164        assert_eq!(text.get_delta().len(), 2);
5165        assert!(text.attached_handler().unwrap().has_decoded_state());
5166    }
5167
5168    #[test]
5169    fn text_lazy_event_queries_match_decoded_state() {
5170        let loro = LoroDoc::new_auto_commit();
5171        let text = loro.get_text("text");
5172        text.insert(0, "ab😀cd", PosType::Unicode).unwrap();
5173        text.mark(1, 4, "bold", true.into(), PosType::Unicode)
5174            .unwrap();
5175        text.mark(2, 3, "link", "x".into(), PosType::Unicode)
5176            .unwrap();
5177
5178        let lazy_doc = LoroDoc::new();
5179        lazy_doc
5180            .import(&loro.export(ExportMode::snapshot()).unwrap())
5181            .unwrap();
5182        let lazy_text = lazy_doc.get_text("text");
5183
5184        let decoded_doc = LoroDoc::new();
5185        decoded_doc
5186            .import(&loro.export(ExportMode::snapshot()).unwrap())
5187            .unwrap();
5188        let decoded_text = decoded_doc.get_text("text");
5189        decoded_text.get_delta();
5190
5191        assert!(!lazy_text.attached_handler().unwrap().has_decoded_state());
5192        assert!(decoded_text.attached_handler().unwrap().has_decoded_state());
5193
5194        for pos_type in [
5195            PosType::Event,
5196            PosType::Unicode,
5197            PosType::Utf16,
5198            PosType::Bytes,
5199        ] {
5200            assert_eq!(lazy_text.len(pos_type), decoded_text.len(pos_type));
5201            for pos in 0..=decoded_text.len(pos_type) {
5202                assert_eq!(
5203                    lazy_text.convert_pos(pos, pos_type, PosType::Unicode),
5204                    decoded_text.convert_pos(pos, pos_type, PosType::Unicode),
5205                    "convert {pos_type:?} pos {pos} to unicode"
5206                );
5207                assert_eq!(
5208                    lazy_text.convert_pos(pos, pos_type, PosType::Event),
5209                    decoded_text.convert_pos(pos, pos_type, PosType::Event),
5210                    "convert {pos_type:?} pos {pos} to event"
5211                );
5212                if pos < decoded_text.len(pos_type) {
5213                    assert_eq!(
5214                        lazy_text.char_at(pos, pos_type),
5215                        decoded_text.char_at(pos, pos_type),
5216                        "char_at {pos_type:?} pos {pos}"
5217                    );
5218                }
5219                for end in pos..=decoded_text.len(pos_type) {
5220                    assert_eq!(
5221                        lazy_text.slice(pos, end, pos_type),
5222                        decoded_text.slice(pos, end, pos_type),
5223                        "slice {pos_type:?} {pos}..{end}"
5224                    );
5225                }
5226            }
5227        }
5228    }
5229
5230    #[test]
5231    fn deep_value_with_id_uses_lazy_values_for_snapshot_roots() {
5232        let loro = LoroDoc::new_auto_commit();
5233        let text = loro.get_text("text");
5234        text.insert(0, "hello", PosType::Unicode).unwrap();
5235        let map = loro.get_map("map");
5236        map.insert("key", "value").unwrap();
5237        let list = loro.get_list("list");
5238        list.push("item").unwrap();
5239
5240        let restored = LoroDoc::new();
5241        restored
5242            .import(&loro.export(ExportMode::snapshot()).unwrap())
5243            .unwrap();
5244        let text = restored.get_text("text");
5245        let map = restored.get_map("map");
5246        let list = restored.get_list("list");
5247
5248        let value = restored.get_deep_value_with_id();
5249        assert_eq!(value["text"]["value"], "hello".into());
5250        assert_eq!(value["map"]["value"]["key"], "value".into());
5251        assert_eq!(value["list"]["value"][0], "item".into());
5252        assert!(!text.attached_handler().unwrap().has_decoded_state());
5253        assert!(!map.attached_handler().unwrap().has_decoded_state());
5254        assert!(!list.attached_handler().unwrap().has_decoded_state());
5255    }
5256
5257    #[test]
5258    fn lazy_value_reads_do_not_write_stale_snapshot_after_mutation() {
5259        let loro = LoroDoc::new_auto_commit();
5260        let map = loro.get_map("map");
5261        map.insert("key", "old").unwrap();
5262        let child = map
5263            .insert_container("child", MapHandler::new_detached())
5264            .unwrap();
5265        child.insert("nested", "old").unwrap();
5266        let list = loro.get_list("list");
5267        list.push("old").unwrap();
5268        let child_list = list.push_container(ListHandler::new_detached()).unwrap();
5269        child_list.push("nested-old").unwrap();
5270
5271        let restored = LoroDoc::new();
5272        restored
5273            .import(&loro.export(ExportMode::snapshot()).unwrap())
5274            .unwrap();
5275        let map = restored.get_map("map");
5276        let list = restored.get_list("list");
5277
5278        assert_eq!(map.get("key").unwrap(), "old".into());
5279        assert_eq!(list.get(0).unwrap(), "old".into());
5280        let child = match map.get_("child").unwrap() {
5281            ValueOrHandler::Handler(handler) => handler.into_map().unwrap(),
5282            ValueOrHandler::Value(value) => panic!("expected child map, got {value:?}"),
5283        };
5284        let child_list = match list.get_(1).unwrap() {
5285            ValueOrHandler::Handler(handler) => handler.into_list().unwrap(),
5286            ValueOrHandler::Value(value) => panic!("expected child list, got {value:?}"),
5287        };
5288
5289        map.insert("key", "new").unwrap();
5290        child.insert("nested", "new").unwrap();
5291        list.delete(0, 1).unwrap();
5292        list.insert(0, "new").unwrap();
5293        child_list.delete(0, 1).unwrap();
5294        child_list.insert(0, "nested-new").unwrap();
5295        restored.commit_then_renew();
5296
5297        let roundtrip = LoroDoc::new();
5298        roundtrip
5299            .import(&restored.export(ExportMode::snapshot()).unwrap())
5300            .unwrap();
5301        assert_eq!(
5302            roundtrip.get_deep_value().to_json_value(),
5303            serde_json::json!({
5304                "map": { "key": "new", "child": { "nested": "new" } },
5305                "list": ["new", ["nested-new"]]
5306            })
5307        );
5308    }
5309
5310    #[test]
5311    fn fast_snapshot_with_trailing_bytes_is_rejected_on_import() {
5312        let loro = LoroDoc::new_auto_commit();
5313        let map = loro.get_map("map");
5314        map.insert("key", "value").unwrap();
5315        let mut snapshot = loro.export(ExportMode::snapshot()).unwrap();
5316        snapshot.push(0xff);
5317        let corrupted = recheck_fast_blob(snapshot);
5318
5319        let doc = LoroDoc::new();
5320        assert!(doc.import(&corrupted).is_err());
5321    }
5322
5323    #[test]
5324    fn fast_snapshot_with_trailing_bytes_is_rejected_by_meta_decoder() {
5325        let loro = LoroDoc::new_auto_commit();
5326        let map = loro.get_map("map");
5327        map.insert("key", "value").unwrap();
5328        let mut snapshot = loro.export(ExportMode::snapshot()).unwrap();
5329        snapshot.push(0xff);
5330        let corrupted = recheck_fast_blob(snapshot);
5331
5332        assert!(LoroDoc::decode_import_blob_meta(&corrupted, true).is_err());
5333    }
5334
5335    #[test]
5336    fn fast_snapshot_empty_sstable_meta_is_rejected_on_import() {
5337        let loro = LoroDoc::new_auto_commit();
5338        let map = loro.get_map("map");
5339        map.insert("key", "value").unwrap();
5340        let snapshot = loro.export(ExportMode::snapshot()).unwrap();
5341
5342        let mut malformed_state = Vec::new();
5343        malformed_state.extend_from_slice(b"LORO");
5344        malformed_state.push(0);
5345        malformed_state.extend_from_slice(&0u32.to_le_bytes());
5346        let checksum = xxhash_rust::xxh32::xxh32(&[], u32::from_le_bytes(*b"LORO"));
5347        malformed_state.extend_from_slice(&checksum.to_le_bytes());
5348        malformed_state.extend_from_slice(&5u32.to_le_bytes());
5349        let corrupted = replace_fast_snapshot_state_bytes(snapshot, &malformed_state);
5350
5351        let doc = LoroDoc::new();
5352        assert!(doc.import(&corrupted).is_err());
5353    }
5354
5355    #[test]
5356    fn tree_meta() {
5357        let loro = LoroDoc::new_auto_commit();
5358        loro.set_peer_id(1).unwrap();
5359        let tree = loro.get_tree("root");
5360        let id = tree.create(TreeParentId::Root).unwrap();
5361        let meta = tree.get_meta(id).unwrap();
5362        meta.insert("a", 123).unwrap();
5363        loro.commit_then_renew();
5364        let meta = tree.get_meta(id).unwrap();
5365        assert_eq!(meta.get("a").unwrap(), 123.into());
5366        assert_eq!(
5367            json!([{"parent":null,"meta":{"a":123},"id":"0@1","index":0,"children":[],"fractional_index":"80"}]),
5368            tree.get_deep_value().to_json_value()
5369        );
5370        let bytes = loro.export(ExportMode::snapshot()).unwrap();
5371        let loro2 = LoroDoc::new();
5372        loro2.import(&bytes).unwrap();
5373    }
5374
5375    #[test]
5376    fn tree_meta_event() {
5377        use std::sync::Arc;
5378        let loro = LoroDoc::new_auto_commit();
5379        let tree = loro.get_tree("root");
5380        let text = loro.get_text("text");
5381
5382        let id = tree.create(TreeParentId::Root).unwrap();
5383        let meta = tree.get_meta(id).unwrap();
5384        meta.insert("a", 1).unwrap();
5385        text.insert(0, "abc", PosType::Unicode).unwrap();
5386        let _id2 = tree.create(TreeParentId::Root).unwrap();
5387        meta.insert("b", 2).unwrap();
5388
5389        let loro2 = LoroDoc::new_auto_commit();
5390        let _g = loro2.subscribe_root(Arc::new(|e| {
5391            println!("{} {:?} ", e.event_meta.by, e.event_meta.diff)
5392        }));
5393        loro2
5394            .import(&loro.export(ExportMode::all_updates()).unwrap())
5395            .unwrap();
5396        assert_eq!(loro.get_deep_value(), loro2.get_deep_value());
5397    }
5398
5399    #[test]
5400    fn richtext_apply_delta() {
5401        let loro = LoroDoc::new_auto_commit();
5402        let text = loro.get_text("text");
5403        text.apply_delta(&[TextDelta::Insert {
5404            insert: "Hello World!".into(),
5405            attributes: None,
5406        }])
5407        .unwrap();
5408        dbg!(text.get_richtext_value());
5409        text.apply_delta(&[
5410            TextDelta::Retain {
5411                retain: 6,
5412                attributes: Some(fx_map!("italic".into() => loro_common::LoroValue::Bool(true))),
5413            },
5414            TextDelta::Insert {
5415                insert: "New ".into(),
5416                attributes: Some(fx_map!("bold".into() => loro_common::LoroValue::Bool(true))),
5417            },
5418        ])
5419        .unwrap();
5420        dbg!(text.get_richtext_value());
5421        loro.commit_then_renew();
5422        assert_eq!(
5423            text.get_richtext_value().to_json_value(),
5424            json!([
5425                {"insert": "Hello ", "attributes": {"italic": true}},
5426                {"insert": "New ", "attributes": {"bold": true}},
5427                {"insert": "World!"}
5428
5429            ])
5430        )
5431    }
5432
5433    #[test]
5434    fn richtext_apply_delta_marks_without_growth() {
5435        let loro = LoroDoc::new_auto_commit();
5436        let text = loro.get_text("text");
5437        text.insert(0, "abc", PosType::Unicode).unwrap();
5438
5439        text.apply_delta(&[TextDelta::Retain {
5440            retain: 3,
5441            attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5442        }])
5443        .unwrap();
5444        loro.commit_then_renew();
5445
5446        assert_eq!(text.to_string(), "abc");
5447        assert_eq!(
5448            text.get_richtext_value().to_json_value(),
5449            json!([{"insert": "abc", "attributes": {"bold": true}}])
5450        );
5451    }
5452
5453    #[test]
5454    fn richtext_apply_delta_grows_for_mark_gap() {
5455        let loro = LoroDoc::new_auto_commit();
5456        let text = loro.get_text("text");
5457
5458        text.apply_delta(&[TextDelta::Retain {
5459            retain: 1,
5460            attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5461        }])
5462        .unwrap();
5463        loro.commit_then_renew();
5464
5465        assert_eq!(text.to_string(), "\n");
5466        assert_eq!(
5467            text.get_richtext_value().to_json_value(),
5468            json!([{"insert": "\n", "attributes": {"bold": true}}])
5469        );
5470    }
5471
5472    #[test]
5473    fn richtext_apply_delta_ignores_empty_inserts() {
5474        let loro = LoroDoc::new_auto_commit();
5475        let text = loro.get_text("text");
5476        text.insert(0, "seed", PosType::Unicode).unwrap();
5477
5478        text.apply_delta(&[TextDelta::Insert {
5479            insert: "".into(),
5480            attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5481        }])
5482        .unwrap();
5483        loro.commit_then_renew();
5484
5485        assert_eq!(text.to_string(), "seed");
5486        assert_eq!(
5487            text.get_richtext_value().to_json_value(),
5488            json!([{"insert": "seed"}])
5489        );
5490    }
5491
5492    #[test]
5493    fn handler_trait_dispatch_reports_attached_container_identity() {
5494        let loro = LoroDoc::new_auto_commit();
5495        let handlers = [
5496            (loro.get_text("text").to_handler(), ContainerType::Text),
5497            (loro.get_map("map").to_handler(), ContainerType::Map),
5498            (loro.get_list("list").to_handler(), ContainerType::List),
5499            (
5500                loro.get_movable_list("movable").to_handler(),
5501                ContainerType::MovableList,
5502            ),
5503            (loro.get_tree("tree").to_handler(), ContainerType::Tree),
5504        ];
5505
5506        for (handler, expected_type) in handlers {
5507            assert!(handler.is_attached());
5508            assert!(handler.attached_handler().is_some());
5509            assert!(handler.doc().is_some());
5510            assert!(handler.get_attached().is_some());
5511            assert_eq!(handler.kind(), expected_type);
5512            assert_eq!(handler.c_type(), expected_type);
5513            assert_eq!(handler.id().container_type(), expected_type);
5514            assert_eq!(
5515                Handler::from_handler(handler.clone()).unwrap().c_type(),
5516                expected_type
5517            );
5518
5519            handler.get_value();
5520            handler.get_deep_value();
5521            handler.clear().unwrap();
5522        }
5523    }
5524
5525    #[test]
5526    fn handler_trait_dispatch_reports_detached_container_identity() {
5527        let handlers = [
5528            (
5529                Handler::new_unattached(ContainerType::Text),
5530                ContainerType::Text,
5531            ),
5532            (
5533                Handler::new_unattached(ContainerType::Map),
5534                ContainerType::Map,
5535            ),
5536            (
5537                Handler::new_unattached(ContainerType::List),
5538                ContainerType::List,
5539            ),
5540            (
5541                Handler::new_unattached(ContainerType::MovableList),
5542                ContainerType::MovableList,
5543            ),
5544            (
5545                Handler::new_unattached(ContainerType::Tree),
5546                ContainerType::Tree,
5547            ),
5548        ];
5549
5550        for (handler, expected_type) in handlers {
5551            assert!(!handler.is_attached());
5552            assert!(handler.attached_handler().is_none());
5553            assert!(handler.doc().is_none());
5554            assert!(handler.get_attached().is_none());
5555            assert_eq!(handler.kind(), expected_type);
5556            assert_eq!(handler.c_type(), expected_type);
5557            assert_eq!(handler.id().container_type(), expected_type);
5558            assert_eq!(handler.idx().get_type(), expected_type);
5559            assert_eq!(
5560                Handler::from_handler(handler.clone()).unwrap().c_type(),
5561                expected_type
5562            );
5563        }
5564    }
5565
5566    #[test]
5567    fn attaching_detached_handlers_sets_parent_and_attached_back_reference() {
5568        let loro = LoroDoc::new_auto_commit();
5569
5570        let map = loro.get_map("map");
5571        let detached_text = TextHandler::new_detached();
5572        detached_text
5573            .insert(0, "detached", PosType::Unicode)
5574            .unwrap();
5575        let attached_text = map.insert_container("text", detached_text.clone()).unwrap();
5576        assert!(attached_text.is_attached());
5577        assert_eq!(attached_text.to_string(), "detached");
5578        assert_eq!(attached_text.parent().unwrap().c_type(), ContainerType::Map);
5579        assert_eq!(
5580            detached_text.get_attached().unwrap().id(),
5581            attached_text.id()
5582        );
5583
5584        let list = loro.get_list("list");
5585        let detached_map = MapHandler::new_detached();
5586        detached_map.insert("k", 1_i64).unwrap();
5587        let attached_map = list.insert_container(0, detached_map.clone()).unwrap();
5588        assert!(attached_map.is_attached());
5589        assert_eq!(attached_map.parent().unwrap().c_type(), ContainerType::List);
5590        assert_eq!(detached_map.get_attached().unwrap().id(), attached_map.id());
5591
5592        let movable = loro.get_movable_list("movable");
5593        let detached_list = ListHandler::new_detached();
5594        detached_list.push("item").unwrap();
5595        let attached_list = movable.insert_container(0, detached_list.clone()).unwrap();
5596        assert!(attached_list.is_attached());
5597        assert_eq!(
5598            attached_list.parent().unwrap().c_type(),
5599            ContainerType::MovableList
5600        );
5601        assert_eq!(
5602            detached_list.get_attached().unwrap().id(),
5603            attached_list.id()
5604        );
5605
5606        let nested = attached_map
5607            .insert_container("movable", MovableListHandler::new_detached())
5608            .unwrap();
5609        assert_eq!(nested.parent().unwrap().id(), attached_map.id());
5610    }
5611
5612    #[test]
5613    fn unknown_handler_reports_identity_without_materializing_value() {
5614        let loro = LoroDoc::new_auto_commit();
5615        let id = ContainerID::Root {
5616            name: "unknown".into(),
5617            container_type: ContainerType::Unknown(7),
5618        };
5619        let handler = Handler::new_attached(id.clone(), loro.clone());
5620        let unknown = handler.as_unknown().unwrap();
5621
5622        assert!(unknown.is_attached());
5623        assert_eq!(unknown.kind(), ContainerType::Unknown(7));
5624        assert_eq!(unknown.id(), id);
5625        assert_eq!(unknown.to_handler().c_type(), ContainerType::Unknown(7));
5626        assert!(unknown.doc().is_some());
5627        assert!(!unknown.is_deleted());
5628        assert_eq!(format!("{unknown:?}"), "UnknownHandler");
5629        assert!(unknown.get_attached().is_some());
5630        assert!(super::UnknownHandler::from_handler(handler).is_some());
5631    }
5632}