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