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