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