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