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