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