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                            let translated =
4316                                loro_common::translate_mergeable_marker_value(&inner.id, key.as_ref(), value);
4317                            (key.to_string(), value_to_value_or_handler(inner, translated))
4318                        })
4319                        .collect::<Vec<_>>()
4320                });
4321
4322                for (k, v) in temp.into_iter() {
4323                    f(&k, v.clone());
4324                }
4325            }
4326        }
4327    }
4328
4329    pub fn get_child_handler(&self, key: &str) -> LoroResult<Handler> {
4330        match &self.inner {
4331            MaybeDetached::Detached(m) => {
4332                let m = m.lock();
4333                let value = m.value.get(key).unwrap();
4334                match value {
4335                    ValueOrHandler::Value(v) => Err(LoroError::ArgErr(
4336                        format!("Expected Handler but found {:?}", v).into_boxed_str(),
4337                    )),
4338                    ValueOrHandler::Handler(h) => Ok(h.clone()),
4339                }
4340            }
4341            MaybeDetached::Attached(_) => {
4342                let Some(value) = self.get_(key) else {
4343                    return Err(LoroError::ArgErr(
4344                        format!("Key {key} does not exist").into_boxed_str(),
4345                    ));
4346                };
4347                match value {
4348                    ValueOrHandler::Handler(handler) => Ok(handler),
4349                    ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
4350                        format!("Expected Handler but found {:?}", value).into_boxed_str(),
4351                    )),
4352                }
4353            }
4354        }
4355    }
4356
4357    pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
4358        match &self.inner {
4359            MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
4360                method: "get_deep_value_with_id",
4361            }),
4362            MaybeDetached::Attached(inner) => Ok(inner.with_doc_state(|state| {
4363                state.get_container_deep_value_with_id(inner.container_idx, None)
4364            })),
4365        }
4366    }
4367
4368    pub fn get(&self, key: &str) -> Option<LoroValue> {
4369        match &self.inner {
4370            MaybeDetached::Detached(m) => {
4371                let m = m.lock();
4372                m.value.get(key).map(|v| v.to_value())
4373            }
4374            MaybeDetached::Attached(inner) => {
4375                let value = inner
4376                    .with_doc_state(|state| state.get_map_value_by_key(inner.container_idx, key))?;
4377                Some(loro_common::translate_mergeable_marker_value(&inner.id, key, value))
4378            }
4379        }
4380    }
4381
4382    /// Get the value at given key, if value is a container, return a handler to the container
4383    pub fn get_(&self, key: &str) -> Option<ValueOrHandler> {
4384        match &self.inner {
4385            MaybeDetached::Detached(m) => {
4386                let m = m.lock();
4387                m.value.get(key).cloned()
4388            }
4389            MaybeDetached::Attached(inner) => {
4390                let value = inner
4391                    .with_doc_state(|state| state.get_map_value_by_key(inner.container_idx, key))?;
4392                let value = loro_common::translate_mergeable_marker_value(&inner.id, key, value);
4393                Some(value_to_value_or_handler(inner, value))
4394            }
4395        }
4396    }
4397
4398    pub fn get_or_create_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4399        if let Some(ans) = self.get_(key) {
4400            if let ValueOrHandler::Handler(h) = ans {
4401                let kind = h.kind();
4402                return C::from_handler(h).ok_or_else(move || {
4403                    LoroError::ArgErr(
4404                        format!("Expected value type {} but found {:?}", child.kind(), kind)
4405                            .into_boxed_str(),
4406                    )
4407                });
4408            } else if let ValueOrHandler::Value(LoroValue::Null) = ans {
4409                // do nothing
4410            } else {
4411                return Err(LoroError::ArgErr(
4412                    format!("Expected value type {} but found {:?}", child.kind(), ans)
4413                        .into_boxed_str(),
4414                ));
4415            }
4416        }
4417
4418        self.insert_container(key, child)
4419    }
4420
4421    /// Shared implementation for all `ensure_mergeable_*` methods.
4422    ///
4423    /// Computes a deterministic [`ContainerID::Root`] in the mergeable namespace from
4424    /// `(parent.id, key, child.kind())` and constructs the handler from it. Two peers calling this
4425    /// with the same `(parent, key, kind)` receive handlers with identical container ids, which is
4426    /// what makes the child container mergeable on concurrent first-write.
4427    ///
4428    /// # Errors
4429    ///
4430    /// Returns [`LoroError::MisuseDetachedContainer`] when called on a detached handler. The
4431    /// deterministic cid is computed from the parent's cid, which a detached parent does not have
4432    /// yet; falling back to a non-deterministic regular child would silently drop the mergeable
4433    /// guarantee at attach time. Detached callers must attach the parent first.
4434    ///
4435    /// Returns [`LoroError::ArgErr`] if the parent slot already holds a non-mergeable value, or if
4436    /// `C::from_handler` rejects the handler built from the deterministic cid (unreachable by
4437    /// construction; guards against future drift between `from_handler` and `kind`).
4438    fn ensure_mergeable_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4439        let MaybeDetached::Attached(parent) = &self.inner else {
4440            return Err(LoroError::MisuseDetachedContainer {
4441                method: "ensure_mergeable_container",
4442            });
4443        };
4444
4445        // Compare against the raw marker bytes (skipping `MapHandler::get`'s user-facing
4446        // marker → Container translation) so the non-mergeable-occupant guard sees the real
4447        // slot value and the same-kind idempotent-skip can match.
4448        let existing_raw = parent.with_doc_state(|state| {
4449            state.get_map_value_by_key(parent.container_idx, key)
4450        });
4451
4452        // A non-mergeable occupant (scalar, arbitrary binary, regular child container) would be
4453        // silently clobbered by the marker write, so reject rather than overwrite under a
4454        // `get_`-named API. Only the exact binary marker for this `(parent, key, kind)` is
4455        // accepted as an existing mergeable occupant.
4456        if let Some(existing) = &existing_raw {
4457            if !matches!(existing, LoroValue::Null)
4458                && loro_common::parse_mergeable_marker(&parent.id, key, existing).is_none()
4459            {
4460                return Err(LoroError::ArgErr(
4461                    format!(
4462                        "Cannot create a mergeable {} at key {key:?}: the key already holds a non-mergeable value",
4463                        child.kind()
4464                    )
4465                    .into_boxed_str(),
4466                ));
4467            }
4468        }
4469
4470        let cid = ContainerID::new_mergeable(&parent.id, key, child.kind());
4471        let marker = loro_common::mergeable_marker(&parent.id, key, child.kind());
4472
4473        // Idempotent-skip on same marker: `MapHandler::get` translates markers to Container, so
4474        // `insert_with_txn`'s equality check can't see this collision — do it directly.
4475        // A different-kind marker is a deliberate kind change; let the insert through.
4476        if existing_raw.as_ref() != Some(&marker) {
4477            self.insert(key, marker)?;
4478        }
4479
4480        C::from_handler(create_handler(parent, cid.clone())).ok_or_else(|| {
4481            LoroError::ArgErr(
4482                format!(
4483                    "Expected value type {} but found {}",
4484                    child.kind(),
4485                    cid.container_type()
4486                )
4487                .into_boxed_str(),
4488            )
4489        })
4490    }
4491
4492    #[cfg(feature = "counter")]
4493    pub fn ensure_mergeable_counter(&self, key: &str) -> LoroResult<counter::CounterHandler> {
4494        self.ensure_mergeable_container(key, counter::CounterHandler::new_detached())
4495    }
4496
4497    /// Ensure a mergeable Map child exists under `key` and return its handler.
4498    ///
4499    /// Prefer to avoid very deep mergeable-map chains: mergeable cids encode their flattened
4500    /// logical path, so cid size still grows with depth and rides through every op/snapshot
4501    /// reference to it. See [`MERGEABLE_NAMESPACE_PREFIX`](loro_common::MERGEABLE_NAMESPACE_PREFIX).
4502    pub fn ensure_mergeable_map(&self, key: &str) -> LoroResult<MapHandler> {
4503        self.ensure_mergeable_container(key, MapHandler::new_detached())
4504    }
4505
4506    pub fn ensure_mergeable_list(&self, key: &str) -> LoroResult<ListHandler> {
4507        self.ensure_mergeable_container(key, ListHandler::new_detached())
4508    }
4509
4510    pub fn ensure_mergeable_movable_list(&self, key: &str) -> LoroResult<MovableListHandler> {
4511        self.ensure_mergeable_container(key, MovableListHandler::new_detached())
4512    }
4513
4514    pub fn ensure_mergeable_text(&self, key: &str) -> LoroResult<TextHandler> {
4515        self.ensure_mergeable_container(key, TextHandler::new_detached())
4516    }
4517
4518    pub fn ensure_mergeable_tree(&self, key: &str) -> LoroResult<TreeHandler> {
4519        self.ensure_mergeable_container(key, TreeHandler::new_detached())
4520    }
4521
4522    pub fn contains_key(&self, key: &str) -> bool {
4523        self.get(key).is_some()
4524    }
4525
4526    pub fn len(&self) -> usize {
4527        match &self.inner {
4528            MaybeDetached::Detached(m) => m.lock().value.len(),
4529            MaybeDetached::Attached(a) => {
4530                a.with_doc_state(|state| state.get_map_len(a.container_idx))
4531            }
4532        }
4533    }
4534
4535    pub fn is_empty(&self) -> bool {
4536        self.len() == 0
4537    }
4538
4539    pub fn is_deleted(&self) -> bool {
4540        match &self.inner {
4541            MaybeDetached::Detached(_) => false,
4542            MaybeDetached::Attached(a) => a.is_deleted(),
4543        }
4544    }
4545
4546    pub fn clear(&self) -> LoroResult<()> {
4547        match &self.inner {
4548            MaybeDetached::Detached(m) => {
4549                let mut m = m.lock();
4550                m.value.clear();
4551                Ok(())
4552            }
4553            MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4554        }
4555    }
4556
4557    pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4558        let keys: Vec<InternalString> = self.inner.try_attached_state()?.with_state(|state| {
4559            state
4560                .as_map_state()
4561                .unwrap()
4562                .iter()
4563                .map(|(k, _)| k.clone())
4564                .collect()
4565        });
4566
4567        for key in keys {
4568            self.delete_with_txn(txn, &key)?;
4569        }
4570
4571        Ok(())
4572    }
4573
4574    pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
4575        let keys: Vec<InternalString> = match &self.inner {
4576            MaybeDetached::Detached(m) => {
4577                let m = m.lock();
4578                m.value.keys().map(|x| x.as_str().into()).collect()
4579            }
4580            MaybeDetached::Attached(a) => {
4581                a.with_doc_state(|state| state.get_map_keys(a.container_idx))
4582            }
4583        };
4584
4585        keys.into_iter()
4586    }
4587
4588    pub fn values(&self) -> impl Iterator<Item = ValueOrHandler> + '_ {
4589        let values: Vec<ValueOrHandler> = match &self.inner {
4590            MaybeDetached::Detached(m) => {
4591                let m = m.lock();
4592                m.value.values().cloned().collect()
4593            }
4594            MaybeDetached::Attached(a) => a.with_doc_state(|state| {
4595                // A mergeable child's marker lives in the parent map's value table; iterate
4596                // entries (key + value) so the user-boundary translation can resolve each marker
4597                // to the deterministic child cid before wrapping into a handler.
4598                state
4599                    .get_map_entries(a.container_idx)
4600                    .into_iter()
4601                    .map(|(key, value)| {
4602                        let translated =
4603                            loro_common::translate_mergeable_marker_value(&a.id, key.as_ref(), value);
4604                        value_to_value_or_handler(a, translated)
4605                    })
4606                    .collect()
4607            }),
4608        };
4609
4610        values.into_iter()
4611    }
4612
4613    pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
4614        match &self.inner {
4615            MaybeDetached::Detached(_) => None,
4616            MaybeDetached::Attached(a) => a.with_state(|state| {
4617                let m = state.as_map_state().unwrap();
4618                m.get_last_edit_peer(key)
4619            }),
4620        }
4621    }
4622}
4623
4624fn with_txn<R>(doc: &LoroDoc, f: impl FnOnce(&mut Transaction) -> LoroResult<R>) -> LoroResult<R> {
4625    let txn = &doc.txn;
4626    let mut txn = txn.lock();
4627    loop {
4628        if let Some(txn) = &mut *txn {
4629            return f(txn);
4630        } else if cfg!(target_arch = "wasm32") || !doc.can_edit() {
4631            return Err(LoroError::AutoCommitNotStarted);
4632        } else {
4633            drop(txn);
4634            #[cfg(loom)]
4635            loom::thread::yield_now();
4636            doc.start_auto_commit();
4637            txn = doc.txn.lock();
4638        }
4639    }
4640}
4641
4642#[cfg(feature = "counter")]
4643pub mod counter {
4644
4645    use loro_common::LoroResult;
4646
4647    use crate::{
4648        txn::{EventHint, Transaction},
4649        HandlerTrait,
4650    };
4651
4652    use super::{create_handler, Handler, MaybeDetached};
4653
4654    #[derive(Clone)]
4655    pub struct CounterHandler {
4656        pub(super) inner: MaybeDetached<f64>,
4657    }
4658
4659    impl CounterHandler {
4660        pub fn new_detached() -> Self {
4661            Self {
4662                inner: MaybeDetached::new_detached(0.),
4663            }
4664        }
4665
4666        pub fn increment(&self, n: f64) -> LoroResult<()> {
4667            match &self.inner {
4668                MaybeDetached::Detached(d) => {
4669                    let d = &mut d.lock().value;
4670                    *d += n;
4671                    Ok(())
4672                }
4673                MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, n)),
4674            }
4675        }
4676
4677        pub fn decrement(&self, n: f64) -> LoroResult<()> {
4678            match &self.inner {
4679                MaybeDetached::Detached(d) => {
4680                    let d = &mut d.lock().value;
4681                    *d -= n;
4682                    Ok(())
4683                }
4684                MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, -n)),
4685            }
4686        }
4687
4688        fn increment_with_txn(&self, txn: &mut Transaction, n: f64) -> LoroResult<()> {
4689            let inner = self.inner.try_attached_state()?;
4690            txn.apply_local_op(
4691                inner.container_idx,
4692                crate::op::RawOpContent::Counter(n),
4693                EventHint::Counter(n),
4694                &inner.doc,
4695            )
4696        }
4697
4698        pub fn is_deleted(&self) -> bool {
4699            match &self.inner {
4700                MaybeDetached::Detached(_) => false,
4701                MaybeDetached::Attached(a) => a.is_deleted(),
4702            }
4703        }
4704
4705        pub fn clear(&self) -> LoroResult<()> {
4706            self.decrement(self.get_value().into_double().unwrap())
4707        }
4708    }
4709
4710    impl std::fmt::Debug for CounterHandler {
4711        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4712            match &self.inner {
4713                MaybeDetached::Detached(_) => write!(f, "CounterHandler Detached"),
4714                MaybeDetached::Attached(a) => write!(f, "CounterHandler {}", a.id),
4715            }
4716        }
4717    }
4718
4719    impl HandlerTrait for CounterHandler {
4720        fn is_attached(&self) -> bool {
4721            matches!(&self.inner, MaybeDetached::Attached(..))
4722        }
4723
4724        fn attached_handler(&self) -> Option<&crate::BasicHandler> {
4725            self.inner.attached_handler()
4726        }
4727
4728        fn get_value(&self) -> loro_common::LoroValue {
4729            match &self.inner {
4730                MaybeDetached::Detached(t) => {
4731                    let t = t.lock();
4732                    t.value.into()
4733                }
4734                MaybeDetached::Attached(a) => a.get_value(),
4735            }
4736        }
4737
4738        fn get_deep_value(&self) -> loro_common::LoroValue {
4739            self.get_value()
4740        }
4741
4742        fn kind(&self) -> loro_common::ContainerType {
4743            loro_common::ContainerType::Counter
4744        }
4745
4746        fn to_handler(&self) -> super::Handler {
4747            Handler::Counter(self.clone())
4748        }
4749
4750        fn from_handler(h: super::Handler) -> Option<Self> {
4751            match h {
4752                Handler::Counter(x) => Some(x),
4753                _ => None,
4754            }
4755        }
4756
4757        fn attach(
4758            &self,
4759            txn: &mut crate::txn::Transaction,
4760            parent: &crate::BasicHandler,
4761            self_id: loro_common::ContainerID,
4762        ) -> loro_common::LoroResult<Self> {
4763            match &self.inner {
4764                MaybeDetached::Detached(v) => {
4765                    let mut v = v.lock();
4766                    let inner = create_handler(parent, self_id);
4767                    let c = inner.into_counter().unwrap();
4768
4769                    c.increment_with_txn(txn, v.value)?;
4770
4771                    v.attached = c.attached_handler().cloned();
4772                    Ok(c)
4773                }
4774                MaybeDetached::Attached(a) => {
4775                    let new_inner = create_handler(a, self_id);
4776                    let ans = new_inner.into_counter().unwrap();
4777                    let delta = *self.get_value().as_double().unwrap();
4778                    ans.increment_with_txn(txn, delta)?;
4779                    Ok(ans)
4780                }
4781            }
4782        }
4783
4784        fn get_attached(&self) -> Option<Self> {
4785            match &self.inner {
4786                MaybeDetached::Attached(a) => Some(Self {
4787                    inner: MaybeDetached::Attached(a.clone()),
4788                }),
4789                MaybeDetached::Detached(v) => v.lock().attached.clone().map(|x| Self {
4790                    inner: MaybeDetached::Attached(x),
4791                }),
4792            }
4793        }
4794
4795        fn doc(&self) -> Option<crate::LoroDoc> {
4796            match &self.inner {
4797                MaybeDetached::Detached(_) => None,
4798                MaybeDetached::Attached(a) => Some(a.doc()),
4799            }
4800        }
4801    }
4802}
4803
4804#[cfg(test)]
4805mod test {
4806    use std::borrow::Cow;
4807
4808    use super::{
4809        Handler, HandlerTrait, ListHandler, MapHandler, MovableListHandler, TextDelta, TextHandler,
4810        ValueOrHandler,
4811    };
4812    use crate::container::list::list_op::ListOp;
4813    use crate::cursor::PosType;
4814    use crate::loro::ExportMode;
4815    use crate::op::ListSlice;
4816    use crate::state::TreeParentId;
4817    use crate::txn::EventHint;
4818    use crate::version::Frontiers;
4819    use crate::LoroDoc;
4820    use crate::{fx_map, ToJson};
4821    use loro_common::{ContainerID, ContainerType, LoroError, LoroValue, ID};
4822    use serde_json::json;
4823
4824    fn recheck_fast_blob(mut bytes: Vec<u8>) -> Vec<u8> {
4825        let checksum = xxhash_rust::xxh32::xxh32(&bytes[20..], u32::from_le_bytes(*b"LORO"));
4826        bytes[16..20].copy_from_slice(&checksum.to_le_bytes());
4827        bytes
4828    }
4829
4830    fn replace_fast_snapshot_state_bytes(mut snapshot: Vec<u8>, state_bytes: &[u8]) -> Vec<u8> {
4831        let mut body = &snapshot[22..];
4832        let oplog_len = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
4833        body = &body[4 + oplog_len..];
4834        let old_state_len = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
4835        let state_len_pos = 22 + 4 + oplog_len;
4836        let state_start = state_len_pos + 4;
4837        let state_end = state_start + old_state_len;
4838        snapshot[state_len_pos..state_start]
4839            .copy_from_slice(&(state_bytes.len() as u32).to_le_bytes());
4840        snapshot.splice(state_start..state_end, state_bytes.iter().copied());
4841        recheck_fast_blob(snapshot)
4842    }
4843
4844    fn insert_many_with_single_list_op(
4845        txn: &mut crate::txn::Transaction,
4846        list: &crate::handler::ListHandler,
4847        pos: usize,
4848        values: Vec<LoroValue>,
4849    ) {
4850        let len = values.len();
4851        let inner = list.inner.try_attached_state().unwrap();
4852        txn.apply_local_op(
4853            inner.container_idx,
4854            crate::op::RawOpContent::List(ListOp::Insert {
4855                slice: ListSlice::RawData(Cow::Owned(values)),
4856                pos,
4857            }),
4858            EventHint::InsertList {
4859                len: len as u32,
4860                pos,
4861            },
4862            &inner.doc,
4863        )
4864        .unwrap();
4865    }
4866
4867    #[test]
4868    fn richtext_handler() {
4869        let loro = LoroDoc::new();
4870        loro.set_peer_id(1).unwrap();
4871        let loro2 = LoroDoc::new();
4872        loro2.set_peer_id(2).unwrap();
4873
4874        let mut txn = loro.txn().unwrap();
4875        let text = txn.get_text("hello");
4876        text.insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
4877            .unwrap();
4878        txn.commit().unwrap();
4879        let exported = loro.export(ExportMode::all_updates()).unwrap();
4880
4881        loro2.import(&exported).unwrap();
4882        let mut txn = loro2.txn().unwrap();
4883        let text = txn.get_text("hello");
4884        assert_eq!(&**text.get_value().as_string().unwrap(), "hello");
4885        text.insert_with_txn(&mut txn, 5, " world", PosType::Unicode)
4886            .unwrap();
4887        assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
4888        txn.commit().unwrap();
4889
4890        loro.import(&loro2.export(ExportMode::all_updates()).unwrap())
4891            .unwrap();
4892        let txn = loro.txn().unwrap();
4893        let text = txn.get_text("hello");
4894        assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
4895        txn.commit().unwrap();
4896
4897        // test checkout
4898        loro.checkout(&Frontiers::from_id(ID::new(2, 1))).unwrap();
4899        assert_eq!(&**text.get_value().as_string().unwrap(), "hello w");
4900    }
4901
4902    #[test]
4903    fn richtext_handler_concurrent() {
4904        let loro = LoroDoc::new();
4905        let mut txn = loro.txn().unwrap();
4906        let handler = loro.get_text("richtext");
4907        handler
4908            .insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
4909            .unwrap();
4910        txn.commit().unwrap();
4911        for i in 0..100 {
4912            let new_loro = LoroDoc::new();
4913            new_loro
4914                .import(&loro.export(ExportMode::all_updates()).unwrap())
4915                .unwrap();
4916            let mut txn = new_loro.txn().unwrap();
4917            let handler = new_loro.get_text("richtext");
4918            handler
4919                .insert_with_txn(&mut txn, i % 5, &i.to_string(), PosType::Unicode)
4920                .unwrap();
4921            txn.commit().unwrap();
4922            loro.import(
4923                &new_loro
4924                    .export(ExportMode::updates(&loro.oplog_vv()))
4925                    .unwrap(),
4926            )
4927            .unwrap();
4928        }
4929    }
4930
4931    #[test]
4932    fn list_import_batch_stays_consistent_after_repeated_tail_splits() {
4933        let doc_a = LoroDoc::new();
4934        doc_a.set_peer_id(1).unwrap();
4935        let mut txn = doc_a.txn().unwrap();
4936        let list_a = txn.get_list("list");
4937        insert_many_with_single_list_op(
4938            &mut txn,
4939            &list_a,
4940            0,
4941            (0..300).map(|i| LoroValue::I64(i)).collect(),
4942        );
4943        txn.commit().unwrap();
4944
4945        let doc_b = LoroDoc::new();
4946        doc_b.set_peer_id(2).unwrap();
4947        doc_b
4948            .import(&doc_a.export(ExportMode::all_updates()).unwrap())
4949            .unwrap();
4950
4951        let list_b = doc_b.get_list("list");
4952        let mut vv = doc_a.oplog_vv();
4953        let mut updates = Vec::new();
4954        for (i, pos) in [100, 201, 252, 278].into_iter().enumerate() {
4955            list_b.insert(pos, 1000 + i as i64).unwrap();
4956            updates.push(doc_b.export(ExportMode::updates(&vv)).unwrap());
4957            vv = doc_b.oplog_vv();
4958        }
4959
4960        doc_a.import_batch(&updates).unwrap();
4961        doc_a.check_state_diff_calc_consistency_slow();
4962        doc_b.check_state_diff_calc_consistency_slow();
4963        assert_eq!(doc_a.get_deep_value(), doc_b.get_deep_value());
4964    }
4965
4966    #[test]
4967    fn richtext_handler_mark() {
4968        let loro = LoroDoc::new_auto_commit();
4969        let handler = loro.get_text("richtext");
4970        handler.insert(0, "hello world", PosType::Unicode).unwrap();
4971        handler
4972            .mark(0, 5, "bold", true.into(), PosType::Event)
4973            .unwrap();
4974        loro.commit_then_renew();
4975
4976        // assert has bold
4977        let value = handler.get_richtext_value();
4978        assert_eq!(value[0]["insert"], "hello".into());
4979        let meta = value[0]["attributes"].as_map().unwrap();
4980        assert_eq!(meta.len(), 1);
4981        meta.get("bold").unwrap();
4982
4983        let loro2 = LoroDoc::new_auto_commit();
4984        loro2
4985            .import(&loro.export(ExportMode::all_updates()).unwrap())
4986            .unwrap();
4987        let handler2 = loro2.get_text("richtext");
4988        assert_eq!(&**handler2.get_value().as_string().unwrap(), "hello world");
4989
4990        // assert has bold
4991        let value = handler2.get_richtext_value();
4992        assert_eq!(value[0]["insert"], "hello".into());
4993        let meta = value[0]["attributes"].as_map().unwrap();
4994        assert_eq!(meta.len(), 1);
4995        meta.get("bold").unwrap();
4996
4997        // insert after bold should be bold
4998        {
4999            handler2.insert(5, " new", PosType::Unicode).unwrap();
5000            let value = handler2.get_richtext_value();
5001            assert_eq!(
5002                value.to_json_value(),
5003                serde_json::json!([
5004                    {"insert": "hello new", "attributes": {"bold": true}},
5005                    {"insert": " world"}
5006                ])
5007            );
5008        }
5009    }
5010
5011    #[test]
5012    fn richtext_snapshot() {
5013        let loro = LoroDoc::new();
5014        let mut txn = loro.txn().unwrap();
5015        let handler = loro.get_text("richtext");
5016        handler
5017            .insert_with_txn(&mut txn, 0, "hello world", PosType::Unicode)
5018            .unwrap();
5019        handler
5020            .mark_with_txn(&mut txn, 0, 5, "bold", true.into(), PosType::Event)
5021            .unwrap();
5022        txn.commit().unwrap();
5023
5024        let loro2 = LoroDoc::new();
5025        loro2
5026            .import(&loro.export(ExportMode::snapshot()).unwrap())
5027            .unwrap();
5028        let handler2 = loro2.get_text("richtext");
5029        assert_eq!(
5030            handler2.get_richtext_value().to_json_value(),
5031            serde_json::json!([
5032                {"insert": "hello", "attributes": {"bold": true}},
5033                {"insert": " world"}
5034            ])
5035        );
5036    }
5037
5038    #[test]
5039    fn text_snapshot_string_queries_do_not_decode_state() {
5040        let loro = LoroDoc::new_auto_commit();
5041        let text = loro.get_text("text");
5042        text.insert(0, "a😀文", PosType::Unicode).unwrap();
5043        text.mark(1, 3, "bold", true.into(), PosType::Unicode)
5044            .unwrap();
5045
5046        let restored = LoroDoc::new();
5047        restored
5048            .import(&loro.export(ExportMode::snapshot()).unwrap())
5049            .unwrap();
5050        let text = restored.get_text("text");
5051        assert!(!text.attached_handler().unwrap().has_decoded_state());
5052
5053        assert_eq!(text.len_unicode(), 3);
5054        assert_eq!(text.len_utf16(), 4);
5055        assert_eq!(text.len_utf8(), "a😀文".len());
5056        assert_eq!(text.char_at(1, PosType::Unicode).unwrap(), '😀');
5057        assert_eq!(text.slice(1, 3, PosType::Unicode).unwrap(), "😀文");
5058        assert_eq!(
5059            text.convert_pos(2, PosType::Unicode, PosType::Utf16),
5060            Some(3)
5061        );
5062        assert!(matches!(
5063            text.delete_utf16(2, 1),
5064            Err(LoroError::UTF16InUnicodeCodePoint { pos: 2 })
5065        ));
5066        assert!(matches!(
5067            text.delete_utf8(2, 1),
5068            Err(LoroError::UTF8InUnicodeCodePoint { pos: 2 })
5069        ));
5070        assert!(matches!(
5071            text.slice_delta(2, 3, PosType::Utf16),
5072            Err(LoroError::UTF16InUnicodeCodePoint { pos: 2 })
5073        ));
5074        assert!(matches!(
5075            text.slice_delta(2, 3, PosType::Bytes),
5076            Err(LoroError::UTF8InUnicodeCodePoint { pos: 2 })
5077        ));
5078        assert!(!text.attached_handler().unwrap().has_decoded_state());
5079
5080        assert_eq!(text.get_delta().len(), 2);
5081        assert!(text.attached_handler().unwrap().has_decoded_state());
5082    }
5083
5084    #[test]
5085    fn text_lazy_event_queries_match_decoded_state() {
5086        let loro = LoroDoc::new_auto_commit();
5087        let text = loro.get_text("text");
5088        text.insert(0, "ab😀cd", PosType::Unicode).unwrap();
5089        text.mark(1, 4, "bold", true.into(), PosType::Unicode)
5090            .unwrap();
5091        text.mark(2, 3, "link", "x".into(), PosType::Unicode)
5092            .unwrap();
5093
5094        let lazy_doc = LoroDoc::new();
5095        lazy_doc
5096            .import(&loro.export(ExportMode::snapshot()).unwrap())
5097            .unwrap();
5098        let lazy_text = lazy_doc.get_text("text");
5099
5100        let decoded_doc = LoroDoc::new();
5101        decoded_doc
5102            .import(&loro.export(ExportMode::snapshot()).unwrap())
5103            .unwrap();
5104        let decoded_text = decoded_doc.get_text("text");
5105        decoded_text.get_delta();
5106
5107        assert!(!lazy_text.attached_handler().unwrap().has_decoded_state());
5108        assert!(decoded_text.attached_handler().unwrap().has_decoded_state());
5109
5110        for pos_type in [
5111            PosType::Event,
5112            PosType::Unicode,
5113            PosType::Utf16,
5114            PosType::Bytes,
5115        ] {
5116            assert_eq!(lazy_text.len(pos_type), decoded_text.len(pos_type));
5117            for pos in 0..=decoded_text.len(pos_type) {
5118                assert_eq!(
5119                    lazy_text.convert_pos(pos, pos_type, PosType::Unicode),
5120                    decoded_text.convert_pos(pos, pos_type, PosType::Unicode),
5121                    "convert {pos_type:?} pos {pos} to unicode"
5122                );
5123                assert_eq!(
5124                    lazy_text.convert_pos(pos, pos_type, PosType::Event),
5125                    decoded_text.convert_pos(pos, pos_type, PosType::Event),
5126                    "convert {pos_type:?} pos {pos} to event"
5127                );
5128                if pos < decoded_text.len(pos_type) {
5129                    assert_eq!(
5130                        lazy_text.char_at(pos, pos_type),
5131                        decoded_text.char_at(pos, pos_type),
5132                        "char_at {pos_type:?} pos {pos}"
5133                    );
5134                }
5135                for end in pos..=decoded_text.len(pos_type) {
5136                    assert_eq!(
5137                        lazy_text.slice(pos, end, pos_type),
5138                        decoded_text.slice(pos, end, pos_type),
5139                        "slice {pos_type:?} {pos}..{end}"
5140                    );
5141                }
5142            }
5143        }
5144    }
5145
5146    #[test]
5147    fn deep_value_with_id_uses_lazy_values_for_snapshot_roots() {
5148        let loro = LoroDoc::new_auto_commit();
5149        let text = loro.get_text("text");
5150        text.insert(0, "hello", PosType::Unicode).unwrap();
5151        let map = loro.get_map("map");
5152        map.insert("key", "value").unwrap();
5153        let list = loro.get_list("list");
5154        list.push("item").unwrap();
5155
5156        let restored = LoroDoc::new();
5157        restored
5158            .import(&loro.export(ExportMode::snapshot()).unwrap())
5159            .unwrap();
5160        let text = restored.get_text("text");
5161        let map = restored.get_map("map");
5162        let list = restored.get_list("list");
5163
5164        let value = restored.get_deep_value_with_id();
5165        assert_eq!(value["text"]["value"], "hello".into());
5166        assert_eq!(value["map"]["value"]["key"], "value".into());
5167        assert_eq!(value["list"]["value"][0], "item".into());
5168        assert!(!text.attached_handler().unwrap().has_decoded_state());
5169        assert!(!map.attached_handler().unwrap().has_decoded_state());
5170        assert!(!list.attached_handler().unwrap().has_decoded_state());
5171    }
5172
5173    #[test]
5174    fn lazy_value_reads_do_not_write_stale_snapshot_after_mutation() {
5175        let loro = LoroDoc::new_auto_commit();
5176        let map = loro.get_map("map");
5177        map.insert("key", "old").unwrap();
5178        let child = map
5179            .insert_container("child", MapHandler::new_detached())
5180            .unwrap();
5181        child.insert("nested", "old").unwrap();
5182        let list = loro.get_list("list");
5183        list.push("old").unwrap();
5184        let child_list = list.push_container(ListHandler::new_detached()).unwrap();
5185        child_list.push("nested-old").unwrap();
5186
5187        let restored = LoroDoc::new();
5188        restored
5189            .import(&loro.export(ExportMode::snapshot()).unwrap())
5190            .unwrap();
5191        let map = restored.get_map("map");
5192        let list = restored.get_list("list");
5193
5194        assert_eq!(map.get("key").unwrap(), "old".into());
5195        assert_eq!(list.get(0).unwrap(), "old".into());
5196        let child = match map.get_("child").unwrap() {
5197            ValueOrHandler::Handler(handler) => handler.into_map().unwrap(),
5198            ValueOrHandler::Value(value) => panic!("expected child map, got {value:?}"),
5199        };
5200        let child_list = match list.get_(1).unwrap() {
5201            ValueOrHandler::Handler(handler) => handler.into_list().unwrap(),
5202            ValueOrHandler::Value(value) => panic!("expected child list, got {value:?}"),
5203        };
5204
5205        map.insert("key", "new").unwrap();
5206        child.insert("nested", "new").unwrap();
5207        list.delete(0, 1).unwrap();
5208        list.insert(0, "new").unwrap();
5209        child_list.delete(0, 1).unwrap();
5210        child_list.insert(0, "nested-new").unwrap();
5211        restored.commit_then_renew();
5212
5213        let roundtrip = LoroDoc::new();
5214        roundtrip
5215            .import(&restored.export(ExportMode::snapshot()).unwrap())
5216            .unwrap();
5217        assert_eq!(
5218            roundtrip.get_deep_value().to_json_value(),
5219            serde_json::json!({
5220                "map": { "key": "new", "child": { "nested": "new" } },
5221                "list": ["new", ["nested-new"]]
5222            })
5223        );
5224    }
5225
5226    #[test]
5227    fn fast_snapshot_with_trailing_bytes_is_rejected_on_import() {
5228        let loro = LoroDoc::new_auto_commit();
5229        let map = loro.get_map("map");
5230        map.insert("key", "value").unwrap();
5231        let mut snapshot = loro.export(ExportMode::snapshot()).unwrap();
5232        snapshot.push(0xff);
5233        let corrupted = recheck_fast_blob(snapshot);
5234
5235        let doc = LoroDoc::new();
5236        assert!(doc.import(&corrupted).is_err());
5237    }
5238
5239    #[test]
5240    fn fast_snapshot_with_trailing_bytes_is_rejected_by_meta_decoder() {
5241        let loro = LoroDoc::new_auto_commit();
5242        let map = loro.get_map("map");
5243        map.insert("key", "value").unwrap();
5244        let mut snapshot = loro.export(ExportMode::snapshot()).unwrap();
5245        snapshot.push(0xff);
5246        let corrupted = recheck_fast_blob(snapshot);
5247
5248        assert!(LoroDoc::decode_import_blob_meta(&corrupted, true).is_err());
5249    }
5250
5251    #[test]
5252    fn fast_snapshot_empty_sstable_meta_is_rejected_on_import() {
5253        let loro = LoroDoc::new_auto_commit();
5254        let map = loro.get_map("map");
5255        map.insert("key", "value").unwrap();
5256        let snapshot = loro.export(ExportMode::snapshot()).unwrap();
5257
5258        let mut malformed_state = Vec::new();
5259        malformed_state.extend_from_slice(b"LORO");
5260        malformed_state.push(0);
5261        malformed_state.extend_from_slice(&0u32.to_le_bytes());
5262        let checksum = xxhash_rust::xxh32::xxh32(&[], u32::from_le_bytes(*b"LORO"));
5263        malformed_state.extend_from_slice(&checksum.to_le_bytes());
5264        malformed_state.extend_from_slice(&5u32.to_le_bytes());
5265        let corrupted = replace_fast_snapshot_state_bytes(snapshot, &malformed_state);
5266
5267        let doc = LoroDoc::new();
5268        assert!(doc.import(&corrupted).is_err());
5269    }
5270
5271    #[test]
5272    fn tree_meta() {
5273        let loro = LoroDoc::new_auto_commit();
5274        loro.set_peer_id(1).unwrap();
5275        let tree = loro.get_tree("root");
5276        let id = tree.create(TreeParentId::Root).unwrap();
5277        let meta = tree.get_meta(id).unwrap();
5278        meta.insert("a", 123).unwrap();
5279        loro.commit_then_renew();
5280        let meta = tree.get_meta(id).unwrap();
5281        assert_eq!(meta.get("a").unwrap(), 123.into());
5282        assert_eq!(
5283            json!([{"parent":null,"meta":{"a":123},"id":"0@1","index":0,"children":[],"fractional_index":"80"}]),
5284            tree.get_deep_value().to_json_value()
5285        );
5286        let bytes = loro.export(ExportMode::snapshot()).unwrap();
5287        let loro2 = LoroDoc::new();
5288        loro2.import(&bytes).unwrap();
5289    }
5290
5291    #[test]
5292    fn tree_meta_event() {
5293        use std::sync::Arc;
5294        let loro = LoroDoc::new_auto_commit();
5295        let tree = loro.get_tree("root");
5296        let text = loro.get_text("text");
5297
5298        let id = tree.create(TreeParentId::Root).unwrap();
5299        let meta = tree.get_meta(id).unwrap();
5300        meta.insert("a", 1).unwrap();
5301        text.insert(0, "abc", PosType::Unicode).unwrap();
5302        let _id2 = tree.create(TreeParentId::Root).unwrap();
5303        meta.insert("b", 2).unwrap();
5304
5305        let loro2 = LoroDoc::new_auto_commit();
5306        let _g = loro2.subscribe_root(Arc::new(|e| {
5307            println!("{} {:?} ", e.event_meta.by, e.event_meta.diff)
5308        }));
5309        loro2
5310            .import(&loro.export(ExportMode::all_updates()).unwrap())
5311            .unwrap();
5312        assert_eq!(loro.get_deep_value(), loro2.get_deep_value());
5313    }
5314
5315    #[test]
5316    fn richtext_apply_delta() {
5317        let loro = LoroDoc::new_auto_commit();
5318        let text = loro.get_text("text");
5319        text.apply_delta(&[TextDelta::Insert {
5320            insert: "Hello World!".into(),
5321            attributes: None,
5322        }])
5323        .unwrap();
5324        dbg!(text.get_richtext_value());
5325        text.apply_delta(&[
5326            TextDelta::Retain {
5327                retain: 6,
5328                attributes: Some(fx_map!("italic".into() => loro_common::LoroValue::Bool(true))),
5329            },
5330            TextDelta::Insert {
5331                insert: "New ".into(),
5332                attributes: Some(fx_map!("bold".into() => loro_common::LoroValue::Bool(true))),
5333            },
5334        ])
5335        .unwrap();
5336        dbg!(text.get_richtext_value());
5337        loro.commit_then_renew();
5338        assert_eq!(
5339            text.get_richtext_value().to_json_value(),
5340            json!([
5341                {"insert": "Hello ", "attributes": {"italic": true}},
5342                {"insert": "New ", "attributes": {"bold": true}},
5343                {"insert": "World!"}
5344
5345            ])
5346        )
5347    }
5348
5349    #[test]
5350    fn richtext_apply_delta_marks_without_growth() {
5351        let loro = LoroDoc::new_auto_commit();
5352        let text = loro.get_text("text");
5353        text.insert(0, "abc", PosType::Unicode).unwrap();
5354
5355        text.apply_delta(&[TextDelta::Retain {
5356            retain: 3,
5357            attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5358        }])
5359        .unwrap();
5360        loro.commit_then_renew();
5361
5362        assert_eq!(text.to_string(), "abc");
5363        assert_eq!(
5364            text.get_richtext_value().to_json_value(),
5365            json!([{"insert": "abc", "attributes": {"bold": true}}])
5366        );
5367    }
5368
5369    #[test]
5370    fn richtext_apply_delta_grows_for_mark_gap() {
5371        let loro = LoroDoc::new_auto_commit();
5372        let text = loro.get_text("text");
5373
5374        text.apply_delta(&[TextDelta::Retain {
5375            retain: 1,
5376            attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5377        }])
5378        .unwrap();
5379        loro.commit_then_renew();
5380
5381        assert_eq!(text.to_string(), "\n");
5382        assert_eq!(
5383            text.get_richtext_value().to_json_value(),
5384            json!([{"insert": "\n", "attributes": {"bold": true}}])
5385        );
5386    }
5387
5388    #[test]
5389    fn richtext_apply_delta_ignores_empty_inserts() {
5390        let loro = LoroDoc::new_auto_commit();
5391        let text = loro.get_text("text");
5392        text.insert(0, "seed", PosType::Unicode).unwrap();
5393
5394        text.apply_delta(&[TextDelta::Insert {
5395            insert: "".into(),
5396            attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5397        }])
5398        .unwrap();
5399        loro.commit_then_renew();
5400
5401        assert_eq!(text.to_string(), "seed");
5402        assert_eq!(
5403            text.get_richtext_value().to_json_value(),
5404            json!([{"insert": "seed"}])
5405        );
5406    }
5407
5408    #[test]
5409    fn handler_trait_dispatch_reports_attached_container_identity() {
5410        let loro = LoroDoc::new_auto_commit();
5411        let handlers = [
5412            (loro.get_text("text").to_handler(), ContainerType::Text),
5413            (loro.get_map("map").to_handler(), ContainerType::Map),
5414            (loro.get_list("list").to_handler(), ContainerType::List),
5415            (
5416                loro.get_movable_list("movable").to_handler(),
5417                ContainerType::MovableList,
5418            ),
5419            (loro.get_tree("tree").to_handler(), ContainerType::Tree),
5420        ];
5421
5422        for (handler, expected_type) in handlers {
5423            assert!(handler.is_attached());
5424            assert!(handler.attached_handler().is_some());
5425            assert!(handler.doc().is_some());
5426            assert!(handler.get_attached().is_some());
5427            assert_eq!(handler.kind(), expected_type);
5428            assert_eq!(handler.c_type(), expected_type);
5429            assert_eq!(handler.id().container_type(), expected_type);
5430            assert_eq!(
5431                Handler::from_handler(handler.clone()).unwrap().c_type(),
5432                expected_type
5433            );
5434
5435            handler.get_value();
5436            handler.get_deep_value();
5437            handler.clear().unwrap();
5438        }
5439    }
5440
5441    #[test]
5442    fn handler_trait_dispatch_reports_detached_container_identity() {
5443        let handlers = [
5444            (
5445                Handler::new_unattached(ContainerType::Text),
5446                ContainerType::Text,
5447            ),
5448            (
5449                Handler::new_unattached(ContainerType::Map),
5450                ContainerType::Map,
5451            ),
5452            (
5453                Handler::new_unattached(ContainerType::List),
5454                ContainerType::List,
5455            ),
5456            (
5457                Handler::new_unattached(ContainerType::MovableList),
5458                ContainerType::MovableList,
5459            ),
5460            (
5461                Handler::new_unattached(ContainerType::Tree),
5462                ContainerType::Tree,
5463            ),
5464        ];
5465
5466        for (handler, expected_type) in handlers {
5467            assert!(!handler.is_attached());
5468            assert!(handler.attached_handler().is_none());
5469            assert!(handler.doc().is_none());
5470            assert!(handler.get_attached().is_none());
5471            assert_eq!(handler.kind(), expected_type);
5472            assert_eq!(handler.c_type(), expected_type);
5473            assert_eq!(handler.id().container_type(), expected_type);
5474            assert_eq!(handler.idx().get_type(), expected_type);
5475            assert_eq!(
5476                Handler::from_handler(handler.clone()).unwrap().c_type(),
5477                expected_type
5478            );
5479        }
5480    }
5481
5482    #[test]
5483    fn attaching_detached_handlers_sets_parent_and_attached_back_reference() {
5484        let loro = LoroDoc::new_auto_commit();
5485
5486        let map = loro.get_map("map");
5487        let detached_text = TextHandler::new_detached();
5488        detached_text
5489            .insert(0, "detached", PosType::Unicode)
5490            .unwrap();
5491        let attached_text = map.insert_container("text", detached_text.clone()).unwrap();
5492        assert!(attached_text.is_attached());
5493        assert_eq!(attached_text.to_string(), "detached");
5494        assert_eq!(attached_text.parent().unwrap().c_type(), ContainerType::Map);
5495        assert_eq!(
5496            detached_text.get_attached().unwrap().id(),
5497            attached_text.id()
5498        );
5499
5500        let list = loro.get_list("list");
5501        let detached_map = MapHandler::new_detached();
5502        detached_map.insert("k", 1_i64).unwrap();
5503        let attached_map = list.insert_container(0, detached_map.clone()).unwrap();
5504        assert!(attached_map.is_attached());
5505        assert_eq!(attached_map.parent().unwrap().c_type(), ContainerType::List);
5506        assert_eq!(detached_map.get_attached().unwrap().id(), attached_map.id());
5507
5508        let movable = loro.get_movable_list("movable");
5509        let detached_list = ListHandler::new_detached();
5510        detached_list.push("item").unwrap();
5511        let attached_list = movable.insert_container(0, detached_list.clone()).unwrap();
5512        assert!(attached_list.is_attached());
5513        assert_eq!(
5514            attached_list.parent().unwrap().c_type(),
5515            ContainerType::MovableList
5516        );
5517        assert_eq!(
5518            detached_list.get_attached().unwrap().id(),
5519            attached_list.id()
5520        );
5521
5522        let nested = attached_map
5523            .insert_container("movable", MovableListHandler::new_detached())
5524            .unwrap();
5525        assert_eq!(nested.parent().unwrap().id(), attached_map.id());
5526    }
5527
5528    #[test]
5529    fn unknown_handler_reports_identity_without_materializing_value() {
5530        let loro = LoroDoc::new_auto_commit();
5531        let id = ContainerID::Root {
5532            name: "unknown".into(),
5533            container_type: ContainerType::Unknown(7),
5534        };
5535        let handler = Handler::new_attached(id.clone(), loro.clone());
5536        let unknown = handler.as_unknown().unwrap();
5537
5538        assert!(unknown.is_attached());
5539        assert_eq!(unknown.kind(), ContainerType::Unknown(7));
5540        assert_eq!(unknown.id(), id);
5541        assert_eq!(unknown.to_handler().c_type(), ContainerType::Unknown(7));
5542        assert!(unknown.doc().is_some());
5543        assert!(!unknown.is_deleted());
5544        assert_eq!(format!("{unknown:?}"), "UnknownHandler");
5545        assert!(unknown.get_attached().is_some());
5546        assert!(super::UnknownHandler::from_handler(handler).is_some());
5547    }
5548}