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