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 if old_id != new_id {
1135 container_remap.insert(old_id, new_id);
1136 }
1137 };
1138 match self {
1139 Self::Map(x) => {
1140 let diff = diff.into_map().unwrap();
1141 for (key, value) in diff.updated.into_iter() {
1142 match value.value {
1143 Some(ValueOrHandler::Handler(h)) => {
1144 let old_id = h.id();
1145 let new_h = x.insert_container(
1146 &key,
1147 Handler::new_unattached(old_id.container_type()),
1148 )?;
1149 let new_id = new_h.id();
1150 on_container_remap(old_id, new_id);
1151 }
1152 Some(ValueOrHandler::Value(LoroValue::Container(old_id))) => {
1153 let new_h = x.insert_container(
1154 &key,
1155 Handler::new_unattached(old_id.container_type()),
1156 )?;
1157 let new_id = new_h.id();
1158 on_container_remap(old_id, new_id);
1159 }
1160 Some(ValueOrHandler::Value(v)) => {
1161 x.insert_without_skipping(&key, v)?;
1162 }
1163 None => {
1164 x.delete(&key)?;
1165 }
1166 }
1167 }
1168 }
1169 Self::Text(x) => {
1170 let delta = diff.into_text().unwrap();
1171 x.apply_delta(&TextDelta::from_text_diff(delta.iter()))?;
1172 }
1173 Self::List(x) => {
1174 let delta = diff.into_list().unwrap();
1175 x.apply_delta(delta, on_container_remap)?;
1176 }
1177 Self::MovableList(x) => {
1178 let delta = diff.into_list().unwrap();
1179 x.apply_delta(delta, container_remap)?;
1180 }
1181 Self::Tree(x) => {
1182 fn remap_tree_id(
1183 id: &mut TreeID,
1184 container_remap: &FxHashMap<ContainerID, ContainerID>,
1185 ) {
1186 let mut remapped = false;
1187 let mut map_id = id.associated_meta_container();
1188 while let Some(rid) = container_remap.get(&map_id) {
1189 remapped = true;
1190 map_id = rid.clone();
1191 }
1192 if remapped {
1193 *id = TreeID::new(
1194 *map_id.as_normal().unwrap().0,
1195 *map_id.as_normal().unwrap().1,
1196 )
1197 }
1198 }
1199 for diff in diff.into_tree().unwrap().diff {
1200 let mut target = diff.target;
1201 match diff.action {
1202 TreeExternalDiff::Create {
1203 mut parent,
1204 index: _,
1205 position,
1206 } => {
1207 if let TreeParentId::Node(p) = &mut parent {
1208 remap_tree_id(p, container_remap)
1209 }
1210 remap_tree_id(&mut target, container_remap);
1211 if !x.is_node_unexist(&target) && !x.is_node_deleted(&target)? {
1212 x.move_at_with_target_for_apply_diff(parent, position, target)?;
1225 } else {
1226 let new_target = x.__internal__next_tree_id();
1227 if x.create_at_with_target_for_apply_diff(
1228 parent, position, new_target,
1229 )? {
1230 container_remap.insert(
1231 target.associated_meta_container(),
1232 new_target.associated_meta_container(),
1233 );
1234 }
1235 }
1236 }
1237 TreeExternalDiff::Move {
1238 mut parent,
1239 index: _,
1240 position,
1241 old_parent: _,
1242 old_index: _,
1243 } => {
1244 if let TreeParentId::Node(p) = &mut parent {
1245 remap_tree_id(p, container_remap)
1246 }
1247 remap_tree_id(&mut target, container_remap);
1248 if x.is_node_unexist(&target) || x.is_node_deleted(&target).unwrap() {
1250 let new_target = x.__internal__next_tree_id();
1252 if x.create_at_with_target_for_apply_diff(
1253 parent, position, new_target,
1254 )? {
1255 container_remap.insert(
1256 target.associated_meta_container(),
1257 new_target.associated_meta_container(),
1258 );
1259 }
1260 } else {
1261 x.move_at_with_target_for_apply_diff(parent, position, target)?;
1262 }
1263 }
1264 TreeExternalDiff::Delete { .. } => {
1265 remap_tree_id(&mut target, container_remap);
1266 if !x.is_node_deleted(&target).unwrap() {
1267 x.delete(target)?;
1268 }
1269 }
1270 }
1271 }
1272 }
1273 #[cfg(feature = "counter")]
1274 Self::Counter(x) => {
1275 let delta = diff.into_counter().unwrap();
1276 x.increment(delta)?;
1277 }
1278 Self::Unknown(_) => {
1279 }
1281 }
1282
1283 Ok(())
1284 }
1285
1286 pub fn clear(&self) -> LoroResult<()> {
1287 match self {
1288 Handler::Text(text_handler) => text_handler.clear(),
1289 Handler::Map(map_handler) => map_handler.clear(),
1290 Handler::List(list_handler) => list_handler.clear(),
1291 Handler::MovableList(movable_list_handler) => movable_list_handler.clear(),
1292 Handler::Tree(tree_handler) => tree_handler.clear(),
1293 #[cfg(feature = "counter")]
1294 Handler::Counter(counter_handler) => counter_handler.clear(),
1295 Handler::Unknown(_unknown_handler) => Ok(()),
1296 }
1297 }
1298}
1299
1300#[derive(Clone, EnumAsInner, Debug)]
1301pub enum ValueOrHandler {
1302 Value(LoroValue),
1303 Handler(Handler),
1304}
1305
1306impl ValueOrHandler {
1307 pub(crate) fn from_value(value: LoroValue, doc: &Arc<LoroDocInner>) -> Self {
1308 if let LoroValue::Container(c) = value {
1309 ValueOrHandler::Handler(Handler::new_attached(c, LoroDoc::from_inner(doc.clone())))
1310 } else {
1311 ValueOrHandler::Value(value)
1312 }
1313 }
1314
1315 pub(crate) fn to_value(&self) -> LoroValue {
1316 match self {
1317 Self::Value(v) => v.clone(),
1318 Self::Handler(h) => LoroValue::Container(h.id().clone()),
1319 }
1320 }
1321
1322 pub(crate) fn to_deep_value(&self) -> LoroValue {
1323 match self {
1324 Self::Value(v) => v.clone(),
1325 Self::Handler(h) => h.get_deep_value(),
1326 }
1327 }
1328}
1329
1330impl From<LoroValue> for ValueOrHandler {
1331 fn from(value: LoroValue) -> Self {
1332 ValueOrHandler::Value(value)
1333 }
1334}
1335
1336impl TextHandler {
1337 pub fn new_detached() -> Self {
1342 Self {
1343 inner: MaybeDetached::new_detached(RichtextState::default()),
1344 }
1345 }
1346
1347 pub fn version_id(&self) -> Option<usize> {
1351 match &self.inner {
1352 MaybeDetached::Detached(_) => None,
1353 MaybeDetached::Attached(a) => {
1354 Some(a.with_state(|state| state.as_richtext_state_mut().unwrap().get_version_id()))
1355 }
1356 }
1357 }
1358
1359 pub fn get_richtext_value(&self) -> LoroValue {
1360 match &self.inner {
1361 MaybeDetached::Detached(t) => {
1362 let t = t.lock().unwrap();
1363 t.value.get_richtext_value()
1364 }
1365 MaybeDetached::Attached(a) => {
1366 a.with_state(|state| state.as_richtext_state_mut().unwrap().get_richtext_value())
1367 }
1368 }
1369 }
1370
1371 pub fn is_empty(&self) -> bool {
1372 match &self.inner {
1373 MaybeDetached::Detached(t) => t.lock().unwrap().value.is_empty(),
1374 MaybeDetached::Attached(a) => {
1375 a.with_state(|state| state.as_richtext_state_mut().unwrap().is_empty())
1376 }
1377 }
1378 }
1379
1380 pub fn len_utf8(&self) -> usize {
1381 match &self.inner {
1382 MaybeDetached::Detached(t) => {
1383 let t = t.lock().unwrap();
1384 t.value.len_utf8()
1385 }
1386 MaybeDetached::Attached(a) => {
1387 a.with_state(|state| state.as_richtext_state_mut().unwrap().len_utf8())
1388 }
1389 }
1390 }
1391
1392 pub fn len_utf16(&self) -> usize {
1393 match &self.inner {
1394 MaybeDetached::Detached(t) => {
1395 let t = t.lock().unwrap();
1396 t.value.len_utf16()
1397 }
1398 MaybeDetached::Attached(a) => {
1399 a.with_state(|state| state.as_richtext_state_mut().unwrap().len_utf16())
1400 }
1401 }
1402 }
1403
1404 pub fn len_unicode(&self) -> usize {
1405 match &self.inner {
1406 MaybeDetached::Detached(t) => {
1407 let t = t.lock().unwrap();
1408 t.value.len_unicode()
1409 }
1410 MaybeDetached::Attached(a) => {
1411 a.with_state(|state| state.as_richtext_state_mut().unwrap().len_unicode())
1412 }
1413 }
1414 }
1415
1416 pub fn len_event(&self) -> usize {
1419 if cfg!(feature = "wasm") {
1420 self.len_utf16()
1421 } else {
1422 self.len_unicode()
1423 }
1424 }
1425
1426 fn len(&self, pos_type: PosType) -> usize {
1427 match &self.inner {
1428 MaybeDetached::Detached(t) => t.lock().unwrap().value.len(pos_type),
1429 MaybeDetached::Attached(a) => {
1430 a.with_state(|state| state.as_richtext_state_mut().unwrap().len(pos_type))
1431 }
1432 }
1433 }
1434
1435 pub fn diagnose(&self) {
1436 match &self.inner {
1437 MaybeDetached::Detached(t) => {
1438 let t = t.lock().unwrap();
1439 t.value.diagnose();
1440 }
1441 MaybeDetached::Attached(a) => {
1442 a.with_state(|state| state.as_richtext_state_mut().unwrap().diagnose());
1443 }
1444 }
1445 }
1446
1447 pub fn iter(&self, mut callback: impl FnMut(&str) -> bool) {
1448 match &self.inner {
1449 MaybeDetached::Detached(t) => {
1450 let t = t.lock().unwrap();
1451 for span in t.value.iter() {
1452 if !callback(span.text.as_str()) {
1453 return;
1454 }
1455 }
1456 }
1457 MaybeDetached::Attached(a) => {
1458 a.with_state(|state| {
1459 state.as_richtext_state_mut().unwrap().iter(callback);
1460 });
1461 }
1462 }
1463 }
1464
1465 pub fn char_at(&self, pos: usize, pos_type: PosType) -> LoroResult<char> {
1467 let len = self.len(pos_type);
1468 if pos >= len {
1469 return Err(LoroError::OutOfBound {
1470 pos,
1471 len,
1472 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1473 });
1474 }
1475 if let Ok(c) = match &self.inner {
1476 MaybeDetached::Detached(t) => {
1477 let t = t.lock().unwrap();
1478 let event_pos = match pos_type {
1479 PosType::Event => pos,
1480 _ => t.value.index_to_event_index(pos, pos_type),
1481 };
1482 t.value.get_char_by_event_index(event_pos)
1483 }
1484 MaybeDetached::Attached(a) => a.with_state(|state| {
1485 let state = state.as_richtext_state_mut().unwrap();
1486 let event_pos = match pos_type {
1487 PosType::Event => pos,
1488 _ => state.index_to_event_index(pos, pos_type),
1489 };
1490 state.get_char_by_event_index(event_pos)
1491 }),
1492 } {
1493 Ok(c)
1494 } else {
1495 Err(LoroError::OutOfBound {
1496 pos,
1497 len,
1498 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1499 })
1500 }
1501 }
1502
1503 pub fn slice(
1509 &self,
1510 start_index: usize,
1511 end_index: usize,
1512 pos_type: PosType,
1513 ) -> LoroResult<String> {
1514 self.slice_with_pos_type(start_index, end_index, pos_type)
1515 }
1516
1517 pub fn slice_utf16(&self, start_index: usize, end_index: usize) -> LoroResult<String> {
1518 self.slice(start_index, end_index, PosType::Utf16)
1519 }
1520
1521 fn slice_with_pos_type(
1522 &self,
1523 start_index: usize,
1524 end_index: usize,
1525 pos_type: PosType,
1526 ) -> LoroResult<String> {
1527 if end_index < start_index {
1528 return Err(LoroError::EndIndexLessThanStartIndex {
1529 start: start_index,
1530 end: end_index,
1531 });
1532 }
1533 if start_index == end_index {
1534 return Ok(String::new());
1535 }
1536
1537 let info = || format!("Position: {}:{}", file!(), line!()).into_boxed_str();
1538 match &self.inner {
1539 MaybeDetached::Detached(t) => {
1540 let t = t.lock().unwrap();
1541 let len = t.value.len(pos_type);
1542 if end_index > len {
1543 return Err(LoroError::OutOfBound {
1544 pos: end_index,
1545 len,
1546 info: info(),
1547 });
1548 }
1549 let (start, end) = match pos_type {
1550 PosType::Event => (start_index, end_index),
1551 _ => (
1552 t.value.index_to_event_index(start_index, pos_type),
1553 t.value.index_to_event_index(end_index, pos_type),
1554 ),
1555 };
1556 t.value.get_text_slice_by_event_index(start, end - start)
1557 }
1558 MaybeDetached::Attached(a) => a.with_state(|state| {
1559 let state = state.as_richtext_state_mut().unwrap();
1560 let len = state.len(pos_type);
1561 if end_index > len {
1562 return Err(LoroError::OutOfBound {
1563 pos: end_index,
1564 len,
1565 info: info(),
1566 });
1567 }
1568 let (start, end) = match pos_type {
1569 PosType::Event => (start_index, end_index),
1570 _ => (
1571 state.index_to_event_index(start_index, pos_type),
1572 state.index_to_event_index(end_index, pos_type),
1573 ),
1574 };
1575 state.get_text_slice_by_event_index(start, end - start)
1576 }),
1577 }
1578 }
1579
1580 pub fn slice_delta(
1581 &self,
1582 start_index: usize,
1583 end_index: usize,
1584 pos_type: PosType,
1585 ) -> LoroResult<Vec<TextDelta>> {
1586 match &self.inner {
1587 MaybeDetached::Detached(t) => {
1588 let t = t.lock().unwrap();
1589 let ans = t.value.slice_delta(start_index, end_index, pos_type)?;
1590 Ok(ans
1591 .into_iter()
1592 .map(|(s, a)| TextDelta::Insert {
1593 insert: s,
1594 attributes: a.to_option_map(),
1595 })
1596 .collect())
1597 }
1598 MaybeDetached::Attached(a) => a.with_state(|state| {
1599 let ans = state.as_richtext_state_mut().unwrap().slice_delta(
1600 start_index,
1601 end_index,
1602 pos_type,
1603 )?;
1604 Ok(ans
1605 .into_iter()
1606 .map(|(s, a)| TextDelta::Insert {
1607 insert: s,
1608 attributes: a.to_option_map(),
1609 })
1610 .collect())
1611 }),
1612 }
1613 }
1614
1615 pub fn splice(&self, pos: usize, len: usize, s: &str, pos_type: PosType) -> LoroResult<String> {
1622 let x = self.slice(pos, pos + len, pos_type)?;
1623 self.delete(pos, len, pos_type)?;
1624 self.insert(pos, s, pos_type)?;
1625 Ok(x)
1626 }
1627
1628 pub fn splice_utf8(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1629 self.delete_utf8(pos, len)?;
1631 self.insert_utf8(pos, s)?;
1632 Ok(())
1633 }
1634
1635 pub fn splice_utf16(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1636 self.delete(pos, len, PosType::Utf16)?;
1637 self.insert(pos, s, PosType::Utf16)?;
1638 Ok(())
1639 }
1640
1641 pub fn insert(&self, pos: usize, s: &str, pos_type: PosType) -> LoroResult<()> {
1645 match &self.inner {
1646 MaybeDetached::Detached(t) => {
1647 let mut t = t.lock().unwrap();
1648 let (index, _) = t
1649 .value
1650 .get_entity_index_for_text_insert(pos, pos_type)
1651 .unwrap();
1652 t.value.insert_at_entity_index(
1653 index,
1654 BytesSlice::from_bytes(s.as_bytes()),
1655 IdFull::NONE_ID,
1656 );
1657 Ok(())
1658 }
1659 MaybeDetached::Attached(a) => {
1660 a.with_txn(|txn| self.insert_with_txn(txn, pos, s, pos_type))
1661 }
1662 }
1663 }
1664
1665 pub fn insert_utf8(&self, pos: usize, s: &str) -> LoroResult<()> {
1666 self.insert(pos, s, PosType::Bytes)
1667 }
1668
1669 pub fn insert_utf16(&self, pos: usize, s: &str) -> LoroResult<()> {
1670 self.insert(pos, s, PosType::Utf16)
1671 }
1672
1673 pub fn insert_unicode(&self, pos: usize, s: &str) -> LoroResult<()> {
1674 self.insert(pos, s, PosType::Unicode)
1675 }
1676
1677 pub fn insert_with_txn(
1679 &self,
1680 txn: &mut Transaction,
1681 pos: usize,
1682 s: &str,
1683 pos_type: PosType,
1684 ) -> LoroResult<()> {
1685 self.insert_with_txn_and_attr(txn, pos, s, None, pos_type)?;
1686 Ok(())
1687 }
1688
1689 pub fn insert_with_txn_utf8(
1690 &self,
1691 txn: &mut Transaction,
1692 pos: usize,
1693 s: &str,
1694 ) -> LoroResult<()> {
1695 self.insert_with_txn(txn, pos, s, PosType::Bytes)
1696 }
1697
1698 pub fn delete(&self, pos: usize, len: usize, pos_type: PosType) -> LoroResult<()> {
1702 match &self.inner {
1703 MaybeDetached::Detached(t) => {
1704 let mut t = t.lock().unwrap();
1705 let ranges = t.value.get_text_entity_ranges(pos, len, pos_type)?;
1706 for range in ranges.iter().rev() {
1707 t.value
1708 .drain_by_entity_index(range.entity_start, range.entity_len(), None);
1709 }
1710 Ok(())
1711 }
1712 MaybeDetached::Attached(a) => {
1713 a.with_txn(|txn| self.delete_with_txn(txn, pos, len, pos_type))
1714 }
1715 }
1716 }
1717
1718 pub fn delete_utf8(&self, pos: usize, len: usize) -> LoroResult<()> {
1719 self.delete(pos, len, PosType::Bytes)
1720 }
1721
1722 pub fn delete_utf16(&self, pos: usize, len: usize) -> LoroResult<()> {
1723 self.delete(pos, len, PosType::Utf16)
1724 }
1725
1726 pub fn delete_unicode(&self, pos: usize, len: usize) -> LoroResult<()> {
1727 self.delete(pos, len, PosType::Unicode)
1728 }
1729
1730 fn insert_with_txn_and_attr(
1733 &self,
1734 txn: &mut Transaction,
1735 pos: usize,
1736 s: &str,
1737 attr: Option<&FxHashMap<String, LoroValue>>,
1738 pos_type: PosType,
1739 ) -> Result<Vec<(InternalString, LoroValue)>, LoroError> {
1740 if s.is_empty() {
1741 return Ok(Vec::new());
1742 }
1743
1744 match pos_type {
1745 PosType::Event => {
1746 if pos > self.len_event() {
1747 return Err(LoroError::OutOfBound {
1748 pos,
1749 len: self.len_event(),
1750 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1751 });
1752 }
1753 }
1754 PosType::Bytes => {
1755 if pos > self.len_utf8() {
1756 return Err(LoroError::OutOfBound {
1757 pos,
1758 len: self.len_utf8(),
1759 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1760 });
1761 }
1762 }
1763 PosType::Unicode => {
1764 if pos > self.len_unicode() {
1765 return Err(LoroError::OutOfBound {
1766 pos,
1767 len: self.len_unicode(),
1768 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1769 });
1770 }
1771 }
1772 PosType::Entity => {}
1773 PosType::Utf16 => {
1774 if pos > self.len_utf16() {
1775 return Err(LoroError::OutOfBound {
1776 pos,
1777 len: self.len_utf16(),
1778 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1779 });
1780 }
1781 }
1782 }
1783
1784 let inner = self.inner.try_attached_state()?;
1785 let (entity_index, event_index, styles) = inner.with_state(|state| {
1786 let richtext_state = state.as_richtext_state_mut().unwrap();
1787 let ret = richtext_state.get_entity_index_for_text_insert(pos, pos_type);
1788 let (entity_index, cursor) = match ret {
1789 Err(_) => match pos_type {
1790 PosType::Bytes => {
1791 return (
1792 Err(LoroError::UTF8InUnicodeCodePoint { pos }),
1793 0,
1794 StyleMeta::empty(),
1795 );
1796 }
1797 PosType::Utf16 | PosType::Event => {
1798 return (
1799 Err(LoroError::UTF16InUnicodeCodePoint { pos }),
1800 0,
1801 StyleMeta::empty(),
1802 );
1803 }
1804 _ => unreachable!(),
1805 },
1806 Ok(x) => x,
1807 };
1808 let event_index = if let Some(cursor) = cursor {
1809 if pos_type == PosType::Event {
1810 debug_assert_eq!(
1811 richtext_state.get_event_index_by_cursor(cursor),
1812 pos,
1813 "pos={} cursor={:?} state={:#?}",
1814 pos,
1815 cursor,
1816 &richtext_state
1817 );
1818 pos
1819 } else {
1820 richtext_state.get_event_index_by_cursor(cursor)
1821 }
1822 } else {
1823 assert_eq!(entity_index, 0);
1824 0
1825 };
1826 let styles = richtext_state.get_styles_at_entity_index(entity_index);
1827 (Ok(entity_index), event_index, styles)
1828 });
1829
1830 let entity_index = match entity_index {
1831 Err(x) => return Err(x),
1832 _ => entity_index.unwrap(),
1833 };
1834
1835 let mut override_styles = Vec::new();
1836 if let Some(attr) = attr {
1837 let map: FxHashMap<_, _> = styles.iter().map(|x| (x.0.clone(), x.1.data)).collect();
1839 for (key, style) in map.iter() {
1840 match attr.get(key.deref()) {
1841 Some(v) if v == style => {}
1842 new_style_value => {
1843 let new_style_value = new_style_value.cloned().unwrap_or(LoroValue::Null);
1845 override_styles.push((key.clone(), new_style_value));
1846 }
1847 }
1848 }
1849
1850 for (key, style) in attr.iter() {
1851 let key = key.as_str().into();
1852 if !map.contains_key(&key) {
1853 override_styles.push((key, style.clone()));
1854 }
1855 }
1856 }
1857
1858 let unicode_len = s.chars().count();
1859 let event_len = if cfg!(feature = "wasm") {
1860 count_utf16_len(s.as_bytes())
1861 } else {
1862 unicode_len
1863 };
1864
1865 txn.apply_local_op(
1866 inner.container_idx,
1867 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
1868 slice: ListSlice::RawStr {
1869 str: Cow::Borrowed(s),
1870 unicode_len,
1871 },
1872 pos: entity_index,
1873 }),
1874 EventHint::InsertText {
1875 pos: event_index as u32,
1876 styles,
1877 unicode_len: unicode_len as u32,
1878 event_len: event_len as u32,
1879 },
1880 &inner.doc,
1881 )?;
1882
1883 Ok(override_styles)
1884 }
1885
1886 pub fn delete_with_txn(
1888 &self,
1889 txn: &mut Transaction,
1890 pos: usize,
1891 len: usize,
1892 pos_type: PosType,
1893 ) -> LoroResult<()> {
1894 self.delete_with_txn_inline(txn, pos, len, pos_type)
1895 }
1896
1897 fn delete_with_txn_inline(
1898 &self,
1899 txn: &mut Transaction,
1900 pos: usize,
1901 len: usize,
1902 pos_type: PosType,
1903 ) -> LoroResult<()> {
1904 if len == 0 {
1905 return Ok(());
1906 }
1907
1908 if pos + len > self.len(pos_type) {
1909 error!("pos={} len={} len_event={}", pos, len, self.len_event());
1910 return Err(LoroError::OutOfBound {
1911 pos: pos + len,
1912 len: self.len_event(),
1913 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1914 });
1915 }
1916
1917 let inner = self.inner.try_attached_state()?;
1918 let s = tracing::span!(tracing::Level::INFO, "delete", "pos={} len={}", pos, len);
1919 let _e = s.enter();
1920 let mut event_pos = 0;
1921 let mut event_len = 0;
1922 let ranges = inner.with_state(|state| {
1923 let richtext_state = state.as_richtext_state_mut().unwrap();
1924 event_pos = richtext_state.index_to_event_index(pos, pos_type);
1925 let event_end = richtext_state.index_to_event_index(pos + len, pos_type);
1926 event_len = event_end - event_pos;
1927
1928 richtext_state.get_text_entity_ranges_in_event_index_range(event_pos, event_len)
1929 })?;
1930
1931 let pos = event_pos as isize;
1933 let len = event_len as isize;
1934 let mut event_end = pos + len;
1935 for range in ranges.iter().rev() {
1936 let event_start = event_end - range.event_len as isize;
1937 txn.apply_local_op(
1938 inner.container_idx,
1939 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
1940 range.id_start,
1941 range.entity_start as isize,
1942 range.entity_len() as isize,
1943 ))),
1944 EventHint::DeleteText {
1945 span: DeleteSpan {
1946 pos: event_start,
1947 signed_len: range.event_len as isize,
1948 },
1949 unicode_len: range.entity_len(),
1950 },
1951 &inner.doc,
1952 )?;
1953 event_end = event_start;
1954 }
1955
1956 Ok(())
1957 }
1958
1959 pub fn mark(
1963 &self,
1964 start: usize,
1965 end: usize,
1966 key: impl Into<InternalString>,
1967 value: LoroValue,
1968 pos_type: PosType,
1969 ) -> LoroResult<()> {
1970 match &self.inner {
1971 MaybeDetached::Detached(t) => {
1972 let mut g = t.lock().unwrap();
1973 self.mark_for_detached(&mut g.value, key, &value, start, end, pos_type)
1974 }
1975 MaybeDetached::Attached(a) => {
1976 a.with_txn(|txn| self.mark_with_txn(txn, start, end, key, value, pos_type))
1977 }
1978 }
1979 }
1980
1981 fn mark_for_detached(
1982 &self,
1983 state: &mut RichtextState,
1984 key: impl Into<InternalString>,
1985 value: &LoroValue,
1986 start: usize,
1987 end: usize,
1988 pos_type: PosType,
1989 ) -> Result<(), LoroError> {
1990 let key: InternalString = key.into();
1991 let is_delete = matches!(value, &LoroValue::Null);
1992 if start >= end {
1993 return Err(loro_common::LoroError::ArgErr(
1994 "Start must be less than end".to_string().into_boxed_str(),
1995 ));
1996 }
1997
1998 let len = state.len(pos_type);
1999 if end > len {
2000 return Err(LoroError::OutOfBound {
2001 pos: end,
2002 len,
2003 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2004 });
2005 }
2006 let (entity_range, styles) =
2007 state.get_entity_range_and_text_styles_at_range(start..end, pos_type);
2008 if let Some(styles) = styles {
2009 if styles.has_key_value(&key, value) {
2010 return Ok(());
2011 }
2012 }
2013
2014 let has_target_style =
2015 state.range_has_style_key(entity_range.clone(), &StyleKey::Key(key.clone()));
2016 let missing_style_key = is_delete && !has_target_style;
2017
2018 if missing_style_key {
2019 return Ok(());
2020 }
2021
2022 let style_op = Arc::new(StyleOp {
2023 lamport: 0,
2024 peer: 0,
2025 cnt: 0,
2026 key,
2027 value: value.clone(),
2028 info: if is_delete {
2030 TextStyleInfoFlag::BOLD.to_delete()
2031 } else {
2032 TextStyleInfoFlag::BOLD
2033 },
2034 });
2035 state.mark_with_entity_index(entity_range, style_op);
2036 Ok(())
2037 }
2038
2039 pub fn unmark(
2041 &self,
2042 start: usize,
2043 end: usize,
2044 key: impl Into<InternalString>,
2045 pos_type: PosType,
2046 ) -> LoroResult<()> {
2047 match &self.inner {
2048 MaybeDetached::Detached(t) => self.mark_for_detached(
2049 &mut t.lock().unwrap().value,
2050 key,
2051 &LoroValue::Null,
2052 start,
2053 end,
2054 pos_type,
2055 ),
2056 MaybeDetached::Attached(a) => a.with_txn(|txn| {
2057 self.mark_with_txn(txn, start, end, key, LoroValue::Null, pos_type)
2058 }),
2059 }
2060 }
2061
2062 pub fn mark_with_txn(
2064 &self,
2065 txn: &mut Transaction,
2066 start: usize,
2067 end: usize,
2068 key: impl Into<InternalString>,
2069 value: LoroValue,
2070 pos_type: PosType,
2071 ) -> LoroResult<()> {
2072 if start >= end {
2073 return Err(loro_common::LoroError::ArgErr(
2074 "Start must be less than end".to_string().into_boxed_str(),
2075 ));
2076 }
2077
2078 let inner = self.inner.try_attached_state()?;
2079 let key: InternalString = key.into();
2080 let is_delete = matches!(&value, &LoroValue::Null);
2081
2082 let mut doc_state = inner.doc.state.lock().unwrap();
2083 let len = doc_state.with_state_mut(inner.container_idx, |state| {
2084 state.as_richtext_state_mut().unwrap().len(pos_type)
2085 });
2086
2087 if end > len {
2088 return Err(LoroError::OutOfBound {
2089 pos: end,
2090 len,
2091 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2092 });
2093 }
2094
2095 let (entity_range, skip, missing_style_key, event_start, event_end) = doc_state
2096 .with_state_mut(inner.container_idx, |state| {
2097 let state = state.as_richtext_state_mut().unwrap();
2098 let event_start = state.index_to_event_index(start, pos_type);
2099 let event_end = state.index_to_event_index(end, pos_type);
2100 let (entity_range, styles) =
2101 state.get_entity_range_and_styles_at_range(start..end, pos_type);
2102
2103 let skip = styles
2104 .as_ref()
2105 .map(|styles| styles.has_key_value(&key, &value))
2106 .unwrap_or(false);
2107 let has_target_style = state.has_style_key_in_entity_range(
2108 entity_range.clone(),
2109 &StyleKey::Key(key.clone()),
2110 );
2111 let missing_style_key = is_delete && !has_target_style;
2112
2113 (
2114 entity_range,
2115 skip,
2116 missing_style_key,
2117 event_start,
2118 event_end,
2119 )
2120 });
2121
2122 if skip || missing_style_key {
2123 return Ok(());
2124 }
2125
2126 let entity_start = entity_range.start;
2127 let entity_end = entity_range.end;
2128 let style_config = doc_state.config.text_style_config.try_read().unwrap();
2129 let flag = if is_delete {
2130 style_config
2131 .get_style_flag_for_unmark(&key)
2132 .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2133 } else {
2134 style_config
2135 .get_style_flag(&key)
2136 .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2137 };
2138
2139 drop(style_config);
2140 drop(doc_state);
2141 txn.apply_local_op(
2142 inner.container_idx,
2143 crate::op::RawOpContent::List(ListOp::StyleStart {
2144 start: entity_start as u32,
2145 end: entity_end as u32,
2146 key: key.clone(),
2147 value: value.clone(),
2148 info: flag,
2149 }),
2150 EventHint::Mark {
2151 start: event_start as u32,
2152 end: event_end as u32,
2153 style: crate::container::richtext::Style { key, data: value },
2154 },
2155 &inner.doc,
2156 )?;
2157
2158 txn.apply_local_op(
2159 inner.container_idx,
2160 crate::op::RawOpContent::List(ListOp::StyleEnd),
2161 EventHint::MarkEnd,
2162 &inner.doc,
2163 )?;
2164
2165 Ok(())
2166 }
2167
2168 pub fn check(&self) {
2169 match &self.inner {
2170 MaybeDetached::Detached(t) => {
2171 let t = t.lock().unwrap();
2172 t.value.check_consistency_between_content_and_style_ranges();
2173 }
2174 MaybeDetached::Attached(a) => a.with_state(|state| {
2175 state
2176 .as_richtext_state_mut()
2177 .unwrap()
2178 .check_consistency_between_content_and_style_ranges();
2179 }),
2180 }
2181 }
2182
2183 pub fn apply_delta(&self, delta: &[TextDelta]) -> LoroResult<()> {
2184 match &self.inner {
2185 MaybeDetached::Detached(t) => {
2186 let _t = t.lock().unwrap();
2187 Err(LoroError::NotImplemented(
2189 "`apply_delta` on a detached text container",
2190 ))
2191 }
2192 MaybeDetached::Attached(a) => a.with_txn(|txn| self.apply_delta_with_txn(txn, delta)),
2193 }
2194 }
2195
2196 pub fn apply_delta_with_txn(
2197 &self,
2198 txn: &mut Transaction,
2199 delta: &[TextDelta],
2200 ) -> LoroResult<()> {
2201 let mut index = 0;
2202 struct PendingMark {
2203 start: usize,
2204 end: usize,
2205 attributes: FxHashMap<InternalString, LoroValue>,
2206 }
2207 let mut marks: Vec<PendingMark> = Vec::new();
2208 for d in delta {
2209 match d {
2210 TextDelta::Insert { insert, attributes } => {
2211 let insert_len = event_len(insert.as_str());
2212 if insert_len == 0 {
2213 continue;
2214 }
2215
2216 let mut empty_attr = None;
2217 let attr_ref = attributes.as_ref().unwrap_or_else(|| {
2218 empty_attr = Some(FxHashMap::default());
2219 empty_attr.as_ref().unwrap()
2220 });
2221
2222 let end = index + insert_len;
2223 let override_styles = self.insert_with_txn_and_attr(
2224 txn,
2225 index,
2226 insert.as_str(),
2227 Some(attr_ref),
2228 PosType::Event,
2229 )?;
2230
2231 let mut pending_mark = PendingMark {
2232 start: index,
2233 end,
2234 attributes: FxHashMap::default(),
2235 };
2236 for (key, value) in override_styles {
2237 pending_mark.attributes.insert(key, value);
2238 }
2239 marks.push(pending_mark);
2240 index = end;
2241 }
2242 TextDelta::Delete { delete } => {
2243 self.delete_with_txn(txn, index, *delete, PosType::Event)?;
2244 }
2245 TextDelta::Retain { attributes, retain } => {
2246 let end = index + *retain;
2247 match attributes {
2248 Some(attr) if !attr.is_empty() => {
2249 let mut pending_mark = PendingMark {
2250 start: index,
2251 end,
2252 attributes: FxHashMap::default(),
2253 };
2254 for (key, value) in attr {
2255 pending_mark
2256 .attributes
2257 .insert(key.deref().into(), value.clone());
2258 }
2259 marks.push(pending_mark);
2260 }
2261 _ => {}
2262 }
2263 index = end;
2264 }
2265 }
2266 }
2267
2268 let mut len = self.len_event();
2269 for pending_mark in marks {
2270 if pending_mark.start >= len {
2271 self.insert_with_txn(
2272 txn,
2273 len,
2274 &"\n".repeat(pending_mark.start - len + 1),
2275 PosType::Event,
2276 )?;
2277 len = pending_mark.start;
2278 }
2279
2280 for (key, value) in pending_mark.attributes {
2281 self.mark_with_txn(
2282 txn,
2283 pending_mark.start,
2284 pending_mark.end,
2285 key.deref(),
2286 value,
2287 PosType::Event,
2288 )?;
2289 }
2290 }
2291
2292 Ok(())
2293 }
2294
2295 pub fn update(&self, text: &str, options: UpdateOptions) -> Result<(), UpdateTimeoutError> {
2296 let old_str = self.to_string();
2297 let new = text.chars().map(|x| x as u32).collect::<Vec<u32>>();
2298 let old = old_str.chars().map(|x| x as u32).collect::<Vec<u32>>();
2299 diff(
2300 &mut OperateProxy::new(text_update::DiffHook::new(self, &new)),
2301 options,
2302 &old,
2303 &new,
2304 )?;
2305 Ok(())
2306 }
2307
2308 pub fn update_by_line(
2309 &self,
2310 text: &str,
2311 options: UpdateOptions,
2312 ) -> Result<(), UpdateTimeoutError> {
2313 let hook = text_update::DiffHookForLine::new(self, text);
2314 let old_lines = hook.get_old_arr().to_vec();
2315 let new_lines = hook.get_new_arr().to_vec();
2316 diff(
2317 &mut OperateProxy::new(hook),
2318 options,
2319 &old_lines,
2320 &new_lines,
2321 )
2322 }
2323
2324 #[allow(clippy::inherent_to_string)]
2325 pub fn to_string(&self) -> String {
2326 match &self.inner {
2327 MaybeDetached::Detached(t) => t.lock().unwrap().value.to_string(),
2328 MaybeDetached::Attached(a) => a.get_value().into_string().unwrap().unwrap(),
2329 }
2330 }
2331
2332 pub fn get_cursor(&self, event_index: usize, side: Side) -> Option<Cursor> {
2333 self.get_cursor_internal(event_index, side, true)
2334 }
2335
2336 pub(crate) fn get_cursor_internal(
2338 &self,
2339 index: usize,
2340 side: Side,
2341 get_by_event_index: bool,
2342 ) -> Option<Cursor> {
2343 match &self.inner {
2344 MaybeDetached::Detached(_) => None,
2345 MaybeDetached::Attached(a) => {
2346 let (id, len, origin_pos) = a.with_state(|s| {
2347 let s = s.as_richtext_state_mut().unwrap();
2348 (
2349 s.get_stable_position(index, get_by_event_index),
2350 if get_by_event_index {
2351 s.len_event()
2352 } else {
2353 s.len_unicode()
2354 },
2355 if get_by_event_index {
2356 s.event_index_to_unicode_index(index)
2357 } else {
2358 index
2359 },
2360 )
2361 });
2362
2363 if len == 0 {
2364 return Some(Cursor {
2365 id: None,
2366 container: self.id(),
2367 side: if side == Side::Middle {
2368 Side::Left
2369 } else {
2370 side
2371 },
2372 origin_pos: 0,
2373 });
2374 }
2375
2376 if len <= index {
2377 return Some(Cursor {
2378 id: None,
2379 container: self.id(),
2380 side: Side::Right,
2381 origin_pos: len,
2382 });
2383 }
2384
2385 let id = id?;
2386 Some(Cursor {
2387 id: Some(id),
2388 container: self.id(),
2389 side,
2390 origin_pos,
2391 })
2392 }
2393 }
2394 }
2395
2396 pub(crate) fn convert_entity_index_to_event_index(&self, entity_index: usize) -> usize {
2397 match &self.inner {
2398 MaybeDetached::Detached(s) => s
2399 .lock()
2400 .unwrap()
2401 .value
2402 .entity_index_to_event_index(entity_index),
2403 MaybeDetached::Attached(a) => {
2404 let mut pos = 0;
2405 a.with_state(|s| {
2406 let s = s.as_richtext_state_mut().unwrap();
2407 pos = s.entity_index_to_event_index(entity_index);
2408 });
2409 pos
2410 }
2411 }
2412 }
2413
2414 pub fn get_delta(&self) -> Vec<TextDelta> {
2415 match &self.inner {
2416 MaybeDetached::Detached(s) => {
2417 let mut delta = Vec::new();
2418 for span in s.lock().unwrap().value.iter() {
2419 if span.text.as_str().is_empty() {
2420 continue;
2421 }
2422
2423 let next_attr = span.attributes.to_option_map();
2424 match delta.last_mut() {
2425 Some(TextDelta::Insert { insert, attributes })
2426 if &next_attr == attributes =>
2427 {
2428 insert.push_str(span.text.as_str());
2429 continue;
2430 }
2431 _ => {}
2432 }
2433
2434 delta.push(TextDelta::Insert {
2435 insert: span.text.as_str().to_string(),
2436 attributes: next_attr,
2437 })
2438 }
2439 delta
2440 }
2441 MaybeDetached::Attached(_a) => self
2442 .with_state(|state| {
2443 let state = state.as_richtext_state_mut().unwrap();
2444 Ok(state.get_delta())
2445 })
2446 .unwrap(),
2447 }
2448 }
2449
2450 pub fn is_deleted(&self) -> bool {
2451 match &self.inner {
2452 MaybeDetached::Detached(_) => false,
2453 MaybeDetached::Attached(a) => a.is_deleted(),
2454 }
2455 }
2456
2457 pub fn push_str(&self, s: &str) -> LoroResult<()> {
2458 self.insert_utf8(self.len_utf8(), s)
2459 }
2460
2461 pub fn clear(&self) -> LoroResult<()> {
2462 match &self.inner {
2463 MaybeDetached::Detached(mutex) => {
2464 let mut t = mutex.lock().unwrap();
2465 let len = t.value.len_unicode();
2466 let ranges = t.value.get_text_entity_ranges(0, len, PosType::Unicode)?;
2467 for range in ranges.iter().rev() {
2468 t.value
2469 .drain_by_entity_index(range.entity_start, range.entity_len(), None);
2470 }
2471 Ok(())
2472 }
2473 MaybeDetached::Attached(a) => a.with_txn(|txn| {
2474 let len = a.with_state(|s| s.as_richtext_state_mut().unwrap().len_unicode());
2475 self.delete_with_txn_inline(txn, 0, len, PosType::Unicode)
2476 }),
2477 }
2478 }
2479
2480 pub fn convert_pos(&self, index: usize, from: PosType, to: PosType) -> Option<usize> {
2485 if from == to {
2486 return Some(index);
2487 }
2488
2489 if matches!(from, PosType::Entity) || matches!(to, PosType::Entity) {
2490 return None;
2491 }
2492
2493 let (event_index, unicode_index) = match &self.inner {
2495 MaybeDetached::Detached(t) => {
2496 let t = t.lock().unwrap();
2497 if index > t.value.len(from) {
2498 return None;
2499 }
2500 let event_index = if from == PosType::Event {
2501 index
2502 } else {
2503 t.value.index_to_event_index(index, from)
2504 };
2505 let unicode_index = if from == PosType::Unicode {
2506 index
2507 } else {
2508 t.value.event_index_to_unicode_index(event_index)
2509 };
2510 (event_index, unicode_index)
2511 }
2512 MaybeDetached::Attached(a) => {
2513 let res: Option<(usize, usize)> = a.with_state(|state| {
2514 let state = state.as_richtext_state_mut().unwrap();
2515 if index > state.len(from) {
2516 return None;
2517 }
2518
2519 let event_index = if from == PosType::Event {
2520 index
2521 } else {
2522 state.index_to_event_index(index, from)
2523 };
2524 let unicode_index = if from == PosType::Unicode {
2525 index
2526 } else {
2527 state.event_index_to_unicode_index(event_index)
2528 };
2529 Some((event_index, unicode_index))
2530 });
2531
2532 match res {
2533 Some(v) => v,
2534 None => return None,
2535 }
2536 }
2537 };
2538
2539 let result = match to {
2540 PosType::Unicode => Some(unicode_index),
2541 PosType::Event => Some(event_index),
2542 PosType::Bytes | PosType::Utf16 => {
2543 let prefix = match &self.inner {
2545 MaybeDetached::Detached(t) => {
2546 let t = t.lock().unwrap();
2547 if event_index > t.value.len_event() {
2548 return None;
2549 }
2550 t.value.get_text_slice_by_event_index(0, event_index).ok()?
2551 }
2552 MaybeDetached::Attached(a) => {
2553 let res: Result<String, ()> = a.with_state(|state| {
2554 let state = state.as_richtext_state_mut().unwrap();
2555 if event_index > state.len_event() {
2556 return Err(());
2557 }
2558 state
2559 .get_text_slice_by_event_index(0, event_index)
2560 .map_err(|_| ())
2561 });
2562
2563 match res {
2564 Ok(v) => v,
2565 Err(_) => return None,
2566 }
2567 }
2568 };
2569
2570 Some(match to {
2571 PosType::Bytes => prefix.len(),
2572 PosType::Utf16 => count_utf16_len(prefix.as_bytes()),
2573 _ => unreachable!(),
2574 })
2575 }
2576 PosType::Entity => None,
2577 };
2578 result
2579 }
2580}
2581
2582fn event_len(s: &str) -> usize {
2583 if cfg!(feature = "wasm") {
2584 count_utf16_len(s.as_bytes())
2585 } else {
2586 s.chars().count()
2587 }
2588}
2589
2590impl ListHandler {
2591 pub fn new_detached() -> Self {
2595 Self {
2596 inner: MaybeDetached::new_detached(Vec::new()),
2597 }
2598 }
2599
2600 pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
2601 match &self.inner {
2602 MaybeDetached::Detached(l) => {
2603 let mut list = l.lock().unwrap();
2604 list.value.insert(pos, ValueOrHandler::Value(v.into()));
2605 Ok(())
2606 }
2607 MaybeDetached::Attached(a) => {
2608 a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
2609 }
2610 }
2611 }
2612
2613 pub fn insert_with_txn(
2614 &self,
2615 txn: &mut Transaction,
2616 pos: usize,
2617 v: LoroValue,
2618 ) -> LoroResult<()> {
2619 if pos > self.len() {
2620 return Err(LoroError::OutOfBound {
2621 pos,
2622 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2623 len: self.len(),
2624 });
2625 }
2626
2627 let inner = self.inner.try_attached_state()?;
2628 if let Some(_container) = v.as_container() {
2629 return Err(LoroError::ArgErr(
2630 INSERT_CONTAINER_VALUE_ARG_ERROR
2631 .to_string()
2632 .into_boxed_str(),
2633 ));
2634 }
2635
2636 txn.apply_local_op(
2637 inner.container_idx,
2638 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
2639 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
2640 pos,
2641 }),
2642 EventHint::InsertList { len: 1, pos },
2643 &inner.doc,
2644 )
2645 }
2646
2647 pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
2648 match &self.inner {
2649 MaybeDetached::Detached(l) => {
2650 let mut list = l.lock().unwrap();
2651 list.value.push(ValueOrHandler::Value(v.into()));
2652 Ok(())
2653 }
2654 MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v.into())),
2655 }
2656 }
2657
2658 pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
2659 let pos = self.len();
2660 self.insert_with_txn(txn, pos, v)
2661 }
2662
2663 pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
2664 match &self.inner {
2665 MaybeDetached::Detached(l) => {
2666 let mut list = l.lock().unwrap();
2667 Ok(list.value.pop().map(|v| v.to_value()))
2668 }
2669 MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
2670 }
2671 }
2672
2673 pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
2674 let len = self.len();
2675 if len == 0 {
2676 return Ok(None);
2677 }
2678
2679 let v = self.get(len - 1);
2680 self.delete_with_txn(txn, len - 1, 1)?;
2681 Ok(v)
2682 }
2683
2684 pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
2685 match &self.inner {
2686 MaybeDetached::Detached(l) => {
2687 let mut list = l.lock().unwrap();
2688 list.value
2689 .insert(pos, ValueOrHandler::Handler(child.to_handler()));
2690 Ok(child)
2691 }
2692 MaybeDetached::Attached(a) => {
2693 a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
2694 }
2695 }
2696 }
2697
2698 pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
2699 self.insert_container(self.len(), child)
2700 }
2701
2702 pub fn insert_container_with_txn<H: HandlerTrait>(
2703 &self,
2704 txn: &mut Transaction,
2705 pos: usize,
2706 child: H,
2707 ) -> LoroResult<H> {
2708 if pos > self.len() {
2709 return Err(LoroError::OutOfBound {
2710 pos,
2711 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2712 len: self.len(),
2713 });
2714 }
2715
2716 let inner = self.inner.try_attached_state()?;
2717 let id = txn.next_id();
2718 let container_id = ContainerID::new_normal(id, child.kind());
2719 let v = LoroValue::Container(container_id.clone());
2720 txn.apply_local_op(
2721 inner.container_idx,
2722 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
2723 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
2724 pos,
2725 }),
2726 EventHint::InsertList { len: 1, pos },
2727 &inner.doc,
2728 )?;
2729 let ans = child.attach(txn, inner, container_id)?;
2730 Ok(ans)
2731 }
2732
2733 pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
2734 match &self.inner {
2735 MaybeDetached::Detached(l) => {
2736 let mut list = l.lock().unwrap();
2737 list.value.drain(pos..pos + len);
2738 Ok(())
2739 }
2740 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
2741 }
2742 }
2743
2744 pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
2745 if len == 0 {
2746 return Ok(());
2747 }
2748
2749 if pos + len > self.len() {
2750 return Err(LoroError::OutOfBound {
2751 pos: pos + len,
2752 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2753 len: self.len(),
2754 });
2755 }
2756
2757 let inner = self.inner.try_attached_state()?;
2758 let ids: Vec<_> = inner.with_state(|state| {
2759 let list = state.as_list_state().unwrap();
2760 (pos..pos + len)
2761 .map(|i| list.get_id_at(i).unwrap())
2762 .collect()
2763 });
2764
2765 for id in ids.into_iter() {
2766 txn.apply_local_op(
2767 inner.container_idx,
2768 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
2769 id.id(),
2770 pos as isize,
2771 1,
2772 ))),
2773 EventHint::DeleteList(DeleteSpan::new(pos as isize, 1)),
2774 &inner.doc,
2775 )?;
2776 }
2777
2778 Ok(())
2779 }
2780
2781 pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
2782 match &self.inner {
2783 MaybeDetached::Detached(l) => {
2784 let list = l.lock().unwrap();
2785 let value = list.value.get(index).ok_or(LoroError::OutOfBound {
2786 pos: index,
2787 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2788 len: list.value.len(),
2789 })?;
2790 match value {
2791 ValueOrHandler::Handler(h) => Ok(h.clone()),
2792 _ => Err(LoroError::ArgErr(
2793 format!(
2794 "Expected container at index {}, but found {:?}",
2795 index, value
2796 )
2797 .into_boxed_str(),
2798 )),
2799 }
2800 }
2801 MaybeDetached::Attached(a) => {
2802 let Some(value) = a.with_state(|state| {
2803 state.as_list_state().as_ref().unwrap().get(index).cloned()
2804 }) else {
2805 return Err(LoroError::OutOfBound {
2806 pos: index,
2807 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2808 len: a.with_state(|state| state.as_list_state().unwrap().len()),
2809 });
2810 };
2811 match value {
2812 LoroValue::Container(id) => Ok(create_handler(a, id)),
2813 _ => Err(LoroError::ArgErr(
2814 format!(
2815 "Expected container at index {}, but found {:?}",
2816 index, value
2817 )
2818 .into_boxed_str(),
2819 )),
2820 }
2821 }
2822 }
2823 }
2824
2825 pub fn len(&self) -> usize {
2826 match &self.inner {
2827 MaybeDetached::Detached(l) => l.lock().unwrap().value.len(),
2828 MaybeDetached::Attached(a) => {
2829 a.with_state(|state| state.as_list_state().unwrap().len())
2830 }
2831 }
2832 }
2833
2834 pub fn is_empty(&self) -> bool {
2835 self.len() == 0
2836 }
2837
2838 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
2839 let inner = self.inner.try_attached_state()?;
2840 Ok(inner.with_doc_state(|state| {
2841 state.get_container_deep_value_with_id(inner.container_idx, None)
2842 }))
2843 }
2844
2845 pub fn get(&self, index: usize) -> Option<LoroValue> {
2846 match &self.inner {
2847 MaybeDetached::Detached(l) => l.lock().unwrap().value.get(index).map(|x| x.to_value()),
2848 MaybeDetached::Attached(a) => a.with_state(|state| {
2849 let a = state.as_list_state().unwrap();
2850 a.get(index).cloned()
2851 }),
2852 }
2853 }
2854
2855 pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
2857 match &self.inner {
2858 MaybeDetached::Detached(l) => {
2859 let l = l.lock().unwrap();
2860 l.value.get(index).cloned()
2861 }
2862 MaybeDetached::Attached(inner) => {
2863 let value =
2864 inner.with_state(|state| state.as_list_state().unwrap().get(index).cloned());
2865 match value {
2866 Some(LoroValue::Container(container_id)) => Some(ValueOrHandler::Handler(
2867 create_handler(inner, container_id.clone()),
2868 )),
2869 Some(value) => Some(ValueOrHandler::Value(value.clone())),
2870 None => None,
2871 }
2872 }
2873 }
2874 }
2875
2876 pub fn for_each<I>(&self, mut f: I)
2877 where
2878 I: FnMut(ValueOrHandler),
2879 {
2880 match &self.inner {
2881 MaybeDetached::Detached(l) => {
2882 let l = l.lock().unwrap();
2883 for v in l.value.iter() {
2884 f(v.clone())
2885 }
2886 }
2887 MaybeDetached::Attached(inner) => {
2888 let mut temp = vec![];
2889 inner.with_state(|state| {
2890 let a = state.as_list_state().unwrap();
2891 for v in a.iter() {
2892 match v {
2893 LoroValue::Container(c) => {
2894 temp.push(ValueOrHandler::Handler(create_handler(
2895 inner,
2896 c.clone(),
2897 )));
2898 }
2899 value => {
2900 temp.push(ValueOrHandler::Value(value.clone()));
2901 }
2902 }
2903 }
2904 });
2905 for v in temp.into_iter() {
2906 f(v);
2907 }
2908 }
2909 }
2910 }
2911
2912 pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
2913 match &self.inner {
2914 MaybeDetached::Detached(_) => None,
2915 MaybeDetached::Attached(a) => {
2916 let (id, len) = a.with_state(|s| {
2917 let l = s.as_list_state().unwrap();
2918 (l.get_id_at(pos), l.len())
2919 });
2920
2921 if len == 0 {
2922 return Some(Cursor {
2923 id: None,
2924 container: self.id(),
2925 side: if side == Side::Middle {
2926 Side::Left
2927 } else {
2928 side
2929 },
2930 origin_pos: 0,
2931 });
2932 }
2933
2934 if len <= pos {
2935 return Some(Cursor {
2936 id: None,
2937 container: self.id(),
2938 side: Side::Right,
2939 origin_pos: len,
2940 });
2941 }
2942
2943 let id = id?;
2944 Some(Cursor {
2945 id: Some(id.id()),
2946 container: self.id(),
2947 side,
2948 origin_pos: pos,
2949 })
2950 }
2951 }
2952 }
2953
2954 fn apply_delta(
2955 &self,
2956 delta: loro_delta::DeltaRope<
2957 loro_delta::array_vec::ArrayVec<ValueOrHandler, 8>,
2958 crate::event::ListDeltaMeta,
2959 >,
2960 on_container_remap: &mut dyn FnMut(ContainerID, ContainerID),
2961 ) -> LoroResult<()> {
2962 match &self.inner {
2963 MaybeDetached::Detached(_) => unimplemented!(),
2964 MaybeDetached::Attached(_) => {
2965 let mut index = 0;
2966 for item in delta.iter() {
2967 match item {
2968 loro_delta::DeltaItem::Retain { len, .. } => {
2969 index += len;
2970 }
2971 loro_delta::DeltaItem::Replace { value, delete, .. } => {
2972 if *delete > 0 {
2973 self.delete(index, *delete)?;
2974 }
2975
2976 for v in value.iter() {
2977 match v {
2978 ValueOrHandler::Value(LoroValue::Container(old_id)) => {
2979 let new_h = self.insert_container(
2980 index,
2981 Handler::new_unattached(old_id.container_type()),
2982 )?;
2983 let new_id = new_h.id();
2984 on_container_remap(old_id.clone(), new_id);
2985 }
2986 ValueOrHandler::Handler(h) => {
2987 let old_id = h.id();
2988 let new_h = self.insert_container(
2989 index,
2990 Handler::new_unattached(old_id.container_type()),
2991 )?;
2992 let new_id = new_h.id();
2993 on_container_remap(old_id, new_id);
2994 }
2995 ValueOrHandler::Value(v) => {
2996 self.insert(index, v.clone())?;
2997 }
2998 }
2999
3000 index += 1;
3001 }
3002 }
3003 }
3004 }
3005 }
3006 }
3007
3008 Ok(())
3009 }
3010
3011 pub fn is_deleted(&self) -> bool {
3012 match &self.inner {
3013 MaybeDetached::Detached(_) => false,
3014 MaybeDetached::Attached(a) => a.is_deleted(),
3015 }
3016 }
3017
3018 pub fn clear(&self) -> LoroResult<()> {
3019 match &self.inner {
3020 MaybeDetached::Detached(l) => {
3021 let mut l = l.lock().unwrap();
3022 l.value.clear();
3023 Ok(())
3024 }
3025 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
3026 }
3027 }
3028
3029 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
3030 self.delete_with_txn(txn, 0, self.len())
3031 }
3032
3033 pub fn get_id_at(&self, pos: usize) -> Option<ID> {
3034 match &self.inner {
3035 MaybeDetached::Detached(_) => None,
3036 MaybeDetached::Attached(a) => a.with_state(|state| {
3037 state
3038 .as_list_state()
3039 .unwrap()
3040 .get_id_at(pos)
3041 .map(|x| x.id())
3042 }),
3043 }
3044 }
3045}
3046
3047impl MovableListHandler {
3048 pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3049 match &self.inner {
3050 MaybeDetached::Detached(d) => {
3051 let mut d = d.lock().unwrap();
3052 if pos > d.value.len() {
3053 return Err(LoroError::OutOfBound {
3054 pos,
3055 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3056 len: d.value.len(),
3057 });
3058 }
3059 d.value.insert(pos, ValueOrHandler::Value(v.into()));
3060 Ok(())
3061 }
3062 MaybeDetached::Attached(a) => {
3063 a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
3064 }
3065 }
3066 }
3067
3068 #[instrument(skip_all)]
3069 pub fn insert_with_txn(
3070 &self,
3071 txn: &mut Transaction,
3072 pos: usize,
3073 v: LoroValue,
3074 ) -> LoroResult<()> {
3075 if pos > self.len() {
3076 return Err(LoroError::OutOfBound {
3077 pos,
3078 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3079 len: self.len(),
3080 });
3081 }
3082
3083 if v.is_container() {
3084 return Err(LoroError::ArgErr(
3085 INSERT_CONTAINER_VALUE_ARG_ERROR
3086 .to_string()
3087 .into_boxed_str(),
3088 ));
3089 }
3090
3091 let op_index = self.with_state(|state| {
3092 let list = state.as_movable_list_state().unwrap();
3093 Ok(list
3094 .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3095 .unwrap())
3096 })?;
3097
3098 let inner = self.inner.try_attached_state()?;
3099 txn.apply_local_op(
3100 inner.container_idx,
3101 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3102 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3103 pos: op_index,
3104 }),
3105 EventHint::InsertList { len: 1, pos },
3106 &inner.doc,
3107 )
3108 }
3109
3110 #[inline]
3111 pub fn mov(&self, from: usize, to: usize) -> LoroResult<()> {
3112 match &self.inner {
3113 MaybeDetached::Detached(d) => {
3114 let mut d = d.lock().unwrap();
3115 if from >= d.value.len() {
3116 return Err(LoroError::OutOfBound {
3117 pos: from,
3118 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3119 len: d.value.len(),
3120 });
3121 }
3122 if to >= d.value.len() {
3123 return Err(LoroError::OutOfBound {
3124 pos: to,
3125 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3126 len: d.value.len(),
3127 });
3128 }
3129 let v = d.value.remove(from);
3130 d.value.insert(to, v);
3131 Ok(())
3132 }
3133 MaybeDetached::Attached(a) => a.with_txn(|txn| self.move_with_txn(txn, from, to)),
3134 }
3135 }
3136
3137 #[instrument(skip_all)]
3139 pub fn move_with_txn(&self, txn: &mut Transaction, from: usize, to: usize) -> LoroResult<()> {
3140 if from == to {
3141 return Ok(());
3142 }
3143
3144 if from >= self.len() {
3145 return Err(LoroError::OutOfBound {
3146 pos: from,
3147 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3148 len: self.len(),
3149 });
3150 }
3151
3152 if to >= self.len() {
3153 return Err(LoroError::OutOfBound {
3154 pos: to,
3155 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3156 len: self.len(),
3157 });
3158 }
3159
3160 let (op_from, op_to, elem_id, value) = self.with_state(|state| {
3161 let list = state.as_movable_list_state().unwrap();
3162 let (elem_id, elem) = list
3163 .get_elem_at_given_pos(from, IndexType::ForUser)
3164 .unwrap();
3165 Ok((
3166 list.convert_index(from, IndexType::ForUser, IndexType::ForOp)
3167 .unwrap(),
3168 list.convert_index(to, IndexType::ForUser, IndexType::ForOp)
3169 .unwrap(),
3170 elem_id,
3171 elem.value().clone(),
3172 ))
3173 })?;
3174
3175 let inner = self.inner.try_attached_state()?;
3176 txn.apply_local_op(
3177 inner.container_idx,
3178 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Move {
3179 from: op_from as u32,
3180 to: op_to as u32,
3181 elem_id: elem_id.to_id(),
3182 }),
3183 EventHint::Move {
3184 value,
3185 from: from as u32,
3186 to: to as u32,
3187 },
3188 &inner.doc,
3189 )
3190 }
3191
3192 pub fn push(&self, v: LoroValue) -> LoroResult<()> {
3193 match &self.inner {
3194 MaybeDetached::Detached(d) => {
3195 let mut d = d.lock().unwrap();
3196 d.value.push(v.into());
3197 Ok(())
3198 }
3199 MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v)),
3200 }
3201 }
3202
3203 pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
3204 let pos = self.len();
3205 self.insert_with_txn(txn, pos, v)
3206 }
3207
3208 pub fn pop_(&self) -> LoroResult<Option<ValueOrHandler>> {
3209 match &self.inner {
3210 MaybeDetached::Detached(d) => {
3211 let mut d = d.lock().unwrap();
3212 Ok(d.value.pop())
3213 }
3214 MaybeDetached::Attached(a) => {
3215 let last = self.len() - 1;
3216 let ans = self.get_(last);
3217 a.with_txn(|txn| self.pop_with_txn(txn))?;
3218 Ok(ans)
3219 }
3220 }
3221 }
3222
3223 pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
3224 match &self.inner {
3225 MaybeDetached::Detached(a) => {
3226 let mut a = a.lock().unwrap();
3227 Ok(a.value.pop().map(|x| x.to_value()))
3228 }
3229 MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
3230 }
3231 }
3232
3233 pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
3234 let len = self.len();
3235 if len == 0 {
3236 return Ok(None);
3237 }
3238
3239 let v = self.get(len - 1);
3240 self.delete_with_txn(txn, len - 1, 1)?;
3241 Ok(v)
3242 }
3243
3244 pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3245 match &self.inner {
3246 MaybeDetached::Detached(d) => {
3247 let mut d = d.lock().unwrap();
3248 if pos > d.value.len() {
3249 return Err(LoroError::OutOfBound {
3250 pos,
3251 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3252 len: d.value.len(),
3253 });
3254 }
3255 d.value
3256 .insert(pos, ValueOrHandler::Handler(child.to_handler()));
3257 Ok(child)
3258 }
3259 MaybeDetached::Attached(a) => {
3260 a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
3261 }
3262 }
3263 }
3264
3265 pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
3266 self.insert_container(self.len(), child)
3267 }
3268
3269 pub fn insert_container_with_txn<H: HandlerTrait>(
3270 &self,
3271 txn: &mut Transaction,
3272 pos: usize,
3273 child: H,
3274 ) -> LoroResult<H> {
3275 if pos > self.len() {
3276 return Err(LoroError::OutOfBound {
3277 pos,
3278 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3279 len: self.len(),
3280 });
3281 }
3282
3283 let op_index = self.with_state(|state| {
3284 let list = state.as_movable_list_state().unwrap();
3285 Ok(list
3286 .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3287 .unwrap())
3288 })?;
3289
3290 let id = txn.next_id();
3291 let container_id = ContainerID::new_normal(id, child.kind());
3292 let v = LoroValue::Container(container_id.clone());
3293 let inner = self.inner.try_attached_state()?;
3294 txn.apply_local_op(
3295 inner.container_idx,
3296 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3297 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3298 pos: op_index,
3299 }),
3300 EventHint::InsertList { len: 1, pos },
3301 &inner.doc,
3302 )?;
3303 child.attach(txn, inner, container_id)
3304 }
3305
3306 pub fn set(&self, index: usize, value: impl Into<LoroValue>) -> LoroResult<()> {
3307 match &self.inner {
3308 MaybeDetached::Detached(d) => {
3309 let mut d = d.lock().unwrap();
3310 if index >= d.value.len() {
3311 return Err(LoroError::OutOfBound {
3312 pos: index,
3313 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3314 len: d.value.len(),
3315 });
3316 }
3317 d.value[index] = ValueOrHandler::Value(value.into());
3318 Ok(())
3319 }
3320 MaybeDetached::Attached(a) => {
3321 a.with_txn(|txn| self.set_with_txn(txn, index, value.into()))
3322 }
3323 }
3324 }
3325
3326 pub fn set_with_txn(
3327 &self,
3328 txn: &mut Transaction,
3329 index: usize,
3330 value: LoroValue,
3331 ) -> LoroResult<()> {
3332 if index >= self.len() {
3333 return Err(LoroError::OutOfBound {
3334 pos: index,
3335 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3336 len: self.len(),
3337 });
3338 }
3339
3340 let inner = self.inner.try_attached_state()?;
3341 let Some(elem_id) = self.with_state(|state| {
3342 let list = state.as_movable_list_state().unwrap();
3343 Ok(list.get_elem_id_at(index, IndexType::ForUser))
3344 })?
3345 else {
3346 unreachable!()
3347 };
3348
3349 let op = crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3350 elem_id: elem_id.to_id(),
3351 value: value.clone(),
3352 });
3353
3354 let hint = EventHint::SetList { index, value };
3355 txn.apply_local_op(inner.container_idx, op, hint, &inner.doc)
3356 }
3357
3358 pub fn set_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3359 match &self.inner {
3360 MaybeDetached::Detached(d) => {
3361 let mut d = d.lock().unwrap();
3362 d.value[pos] = ValueOrHandler::Handler(child.to_handler());
3363 Ok(child)
3364 }
3365 MaybeDetached::Attached(a) => {
3366 a.with_txn(|txn| self.set_container_with_txn(txn, pos, child))
3367 }
3368 }
3369 }
3370
3371 pub fn set_container_with_txn<H: HandlerTrait>(
3372 &self,
3373 txn: &mut Transaction,
3374 pos: usize,
3375 child: H,
3376 ) -> LoroResult<H> {
3377 let id = txn.next_id();
3378 let container_id = ContainerID::new_normal(id, child.kind());
3379 let v = LoroValue::Container(container_id.clone());
3380 let Some(elem_id) = self.with_state(|state| {
3381 let list = state.as_movable_list_state().unwrap();
3382 Ok(list.get_elem_id_at(pos, IndexType::ForUser))
3383 })?
3384 else {
3385 let len = self.len();
3386 if pos >= len {
3387 return Err(LoroError::OutOfBound {
3388 pos,
3389 len,
3390 info: "".into(),
3391 });
3392 } else {
3393 unreachable!()
3394 }
3395 };
3396 let inner = self.inner.try_attached_state()?;
3397 txn.apply_local_op(
3398 inner.container_idx,
3399 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3400 elem_id: elem_id.to_id(),
3401 value: v.clone(),
3402 }),
3403 EventHint::SetList {
3404 index: pos,
3405 value: v,
3406 },
3407 &inner.doc,
3408 )?;
3409
3410 child.attach(txn, inner, container_id)
3411 }
3412
3413 pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3414 match &self.inner {
3415 MaybeDetached::Detached(d) => {
3416 let mut d = d.lock().unwrap();
3417 d.value.drain(pos..pos + len);
3418 Ok(())
3419 }
3420 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
3421 }
3422 }
3423
3424 #[instrument(skip_all)]
3425 pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
3426 if len == 0 {
3427 return Ok(());
3428 }
3429
3430 if pos + len > self.len() {
3431 return Err(LoroError::OutOfBound {
3432 pos: pos + len,
3433 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3434 len: self.len(),
3435 });
3436 }
3437
3438 let (ids, new_poses) = self.with_state(|state| {
3439 let list = state.as_movable_list_state().unwrap();
3440 let ids: Vec<_> = (pos..pos + len)
3441 .map(|i| list.get_list_id_at(i, IndexType::ForUser).unwrap())
3442 .collect();
3443 let poses: Vec<_> = (pos..pos + len)
3444 .map(|user_index| {
3446 let op_index = list
3447 .convert_index(user_index, IndexType::ForUser, IndexType::ForOp)
3448 .unwrap();
3449 assert!(op_index >= user_index);
3450 op_index - (user_index - pos)
3451 })
3452 .collect();
3453 Ok((ids, poses))
3454 })?;
3455
3456 loro_common::info!(?pos, ?len, ?ids, ?new_poses, "delete_with_txn");
3457 let user_pos = pos;
3458 let inner = self.inner.try_attached_state()?;
3459 for (id, op_pos) in ids.into_iter().zip(new_poses.into_iter()) {
3460 txn.apply_local_op(
3461 inner.container_idx,
3462 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
3463 id,
3464 op_pos as isize,
3465 1,
3466 ))),
3467 EventHint::DeleteList(DeleteSpan::new(user_pos as isize, 1)),
3468 &inner.doc,
3469 )?;
3470 }
3471
3472 Ok(())
3473 }
3474
3475 pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
3476 match &self.inner {
3477 MaybeDetached::Detached(l) => {
3478 let list = l.lock().unwrap();
3479 let value = list.value.get(index).ok_or(LoroError::OutOfBound {
3480 pos: index,
3481 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3482 len: list.value.len(),
3483 })?;
3484 match value {
3485 ValueOrHandler::Handler(h) => Ok(h.clone()),
3486 _ => Err(LoroError::ArgErr(
3487 format!(
3488 "Expected container at index {}, but found {:?}",
3489 index, value
3490 )
3491 .into_boxed_str(),
3492 )),
3493 }
3494 }
3495 MaybeDetached::Attached(a) => {
3496 let Some(value) = a.with_state(|state| {
3497 state
3498 .as_movable_list_state()
3499 .as_ref()
3500 .unwrap()
3501 .get(index, IndexType::ForUser)
3502 .cloned()
3503 }) else {
3504 return Err(LoroError::OutOfBound {
3505 pos: index,
3506 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3507 len: a.with_state(|state| state.as_list_state().unwrap().len()),
3508 });
3509 };
3510 match value {
3511 LoroValue::Container(id) => Ok(create_handler(a, id)),
3512 _ => Err(LoroError::ArgErr(
3513 format!(
3514 "Expected container at index {}, but found {:?}",
3515 index, value
3516 )
3517 .into_boxed_str(),
3518 )),
3519 }
3520 }
3521 }
3522 }
3523
3524 pub fn len(&self) -> usize {
3525 match &self.inner {
3526 MaybeDetached::Detached(d) => {
3527 let d = d.lock().unwrap();
3528 d.value.len()
3529 }
3530 MaybeDetached::Attached(a) => {
3531 a.with_state(|state| state.as_movable_list_state().unwrap().len())
3532 }
3533 }
3534 }
3535
3536 pub fn is_empty(&self) -> bool {
3537 self.len() == 0
3538 }
3539
3540 pub fn get_deep_value_with_id(&self) -> LoroValue {
3541 let inner = self.inner.try_attached_state().unwrap();
3542 inner
3543 .doc
3544 .state
3545 .lock()
3546 .unwrap()
3547 .get_container_deep_value_with_id(inner.container_idx, None)
3548 }
3549
3550 pub fn get(&self, index: usize) -> Option<LoroValue> {
3551 match &self.inner {
3552 MaybeDetached::Detached(d) => {
3553 let d = d.lock().unwrap();
3554 d.value.get(index).map(|v| v.to_value())
3555 }
3556 MaybeDetached::Attached(a) => a.with_state(|state| {
3557 let a = state.as_movable_list_state().unwrap();
3558 a.get(index, IndexType::ForUser).cloned()
3559 }),
3560 }
3561 }
3562
3563 pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
3565 match &self.inner {
3566 MaybeDetached::Detached(d) => {
3567 let d = d.lock().unwrap();
3568 d.value.get(index).cloned()
3569 }
3570 MaybeDetached::Attached(m) => m.with_state(|state| {
3571 let a = state.as_movable_list_state().unwrap();
3572 match a.get(index, IndexType::ForUser) {
3573 Some(v) => {
3574 if let LoroValue::Container(c) = v {
3575 Some(ValueOrHandler::Handler(create_handler(m, c.clone())))
3576 } else {
3577 Some(ValueOrHandler::Value(v.clone()))
3578 }
3579 }
3580 None => None,
3581 }
3582 }),
3583 }
3584 }
3585
3586 pub fn for_each<I>(&self, mut f: I)
3587 where
3588 I: FnMut(ValueOrHandler),
3589 {
3590 match &self.inner {
3591 MaybeDetached::Detached(d) => {
3592 let d = d.lock().unwrap();
3593 for v in d.value.iter() {
3594 f(v.clone());
3595 }
3596 }
3597 MaybeDetached::Attached(m) => {
3598 let mut temp = vec![];
3599 m.with_state(|state| {
3600 let a = state.as_movable_list_state().unwrap();
3601 for v in a.iter() {
3602 match v {
3603 LoroValue::Container(c) => {
3604 temp.push(ValueOrHandler::Handler(create_handler(m, c.clone())));
3605 }
3606 value => {
3607 temp.push(ValueOrHandler::Value(value.clone()));
3608 }
3609 }
3610 }
3611 });
3612
3613 for v in temp.into_iter() {
3614 f(v);
3615 }
3616 }
3617 }
3618 }
3619
3620 pub fn log_internal_state(&self) -> String {
3621 match &self.inner {
3622 MaybeDetached::Detached(d) => {
3623 let d = d.lock().unwrap();
3624 format!("{:#?}", &d.value)
3625 }
3626 MaybeDetached::Attached(a) => a.with_state(|state| {
3627 let a = state.as_movable_list_state().unwrap();
3628 format!("{a:#?}")
3629 }),
3630 }
3631 }
3632
3633 pub fn new_detached() -> MovableListHandler {
3634 MovableListHandler {
3635 inner: MaybeDetached::new_detached(Default::default()),
3636 }
3637 }
3638
3639 pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
3640 match &self.inner {
3641 MaybeDetached::Detached(_) => None,
3642 MaybeDetached::Attached(inner) => {
3643 let (id, len) = inner.with_state(|s| {
3644 let l = s.as_movable_list_state().unwrap();
3645 (l.get_list_item_id_at(pos), l.len())
3646 });
3647
3648 if len == 0 {
3649 return Some(Cursor {
3650 id: None,
3651 container: self.id(),
3652 side: if side == Side::Middle {
3653 Side::Left
3654 } else {
3655 side
3656 },
3657 origin_pos: 0,
3658 });
3659 }
3660
3661 if len <= pos {
3662 return Some(Cursor {
3663 id: None,
3664 container: self.id(),
3665 side: Side::Right,
3666 origin_pos: len,
3667 });
3668 }
3669
3670 let id = id?;
3671 Some(Cursor {
3672 id: Some(id.id()),
3673 container: self.id(),
3674 side,
3675 origin_pos: pos,
3676 })
3677 }
3678 }
3679 }
3680
3681 pub(crate) fn op_pos_to_user_pos(&self, new_pos: usize) -> usize {
3682 match &self.inner {
3683 MaybeDetached::Detached(_) => new_pos,
3684 MaybeDetached::Attached(inner) => {
3685 let mut pos = new_pos;
3686 inner.with_state(|s| {
3687 let l = s.as_movable_list_state().unwrap();
3688 pos = l
3689 .convert_index(new_pos, IndexType::ForOp, IndexType::ForUser)
3690 .unwrap_or(l.len());
3691 });
3692 pos
3693 }
3694 }
3695 }
3696
3697 pub fn is_deleted(&self) -> bool {
3698 match &self.inner {
3699 MaybeDetached::Detached(_) => false,
3700 MaybeDetached::Attached(a) => a.is_deleted(),
3701 }
3702 }
3703
3704 pub fn clear(&self) -> LoroResult<()> {
3705 match &self.inner {
3706 MaybeDetached::Detached(d) => {
3707 let mut d = d.lock().unwrap();
3708 d.value.clear();
3709 Ok(())
3710 }
3711 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
3712 }
3713 }
3714
3715 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
3716 self.delete_with_txn(txn, 0, self.len())
3717 }
3718
3719 pub fn get_creator_at(&self, pos: usize) -> Option<PeerID> {
3720 match &self.inner {
3721 MaybeDetached::Detached(_) => None,
3722 MaybeDetached::Attached(a) => {
3723 a.with_state(|state| state.as_movable_list_state().unwrap().get_creator_at(pos))
3724 }
3725 }
3726 }
3727
3728 pub fn get_last_mover_at(&self, pos: usize) -> Option<PeerID> {
3729 match &self.inner {
3730 MaybeDetached::Detached(_) => None,
3731 MaybeDetached::Attached(a) => a.with_state(|state| {
3732 state
3733 .as_movable_list_state()
3734 .unwrap()
3735 .get_last_mover_at(pos)
3736 }),
3737 }
3738 }
3739
3740 pub fn get_last_editor_at(&self, pos: usize) -> Option<PeerID> {
3741 match &self.inner {
3742 MaybeDetached::Detached(_) => None,
3743 MaybeDetached::Attached(a) => a.with_state(|state| {
3744 state
3745 .as_movable_list_state()
3746 .unwrap()
3747 .get_last_editor_at(pos)
3748 }),
3749 }
3750 }
3751}
3752
3753impl MapHandler {
3754 pub fn new_detached() -> Self {
3758 Self {
3759 inner: MaybeDetached::new_detached(Default::default()),
3760 }
3761 }
3762
3763 pub fn insert(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
3764 match &self.inner {
3765 MaybeDetached::Detached(m) => {
3766 let mut m = m.lock().unwrap();
3767 m.value
3768 .insert(key.into(), ValueOrHandler::Value(value.into()));
3769 Ok(())
3770 }
3771 MaybeDetached::Attached(a) => {
3772 a.with_txn(|txn| self.insert_with_txn(txn, key, value.into()))
3773 }
3774 }
3775 }
3776
3777 fn insert_without_skipping(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
3779 match &self.inner {
3780 MaybeDetached::Detached(m) => {
3781 let mut m = m.lock().unwrap();
3782 m.value
3783 .insert(key.into(), ValueOrHandler::Value(value.into()));
3784 Ok(())
3785 }
3786 MaybeDetached::Attached(a) => a.with_txn(|txn| {
3787 let this = &self;
3788 let value = value.into();
3789 if let Some(_value) = value.as_container() {
3790 return Err(LoroError::ArgErr(
3791 INSERT_CONTAINER_VALUE_ARG_ERROR
3792 .to_string()
3793 .into_boxed_str(),
3794 ));
3795 }
3796
3797 let inner = this.inner.try_attached_state()?;
3798 txn.apply_local_op(
3799 inner.container_idx,
3800 crate::op::RawOpContent::Map(crate::container::map::MapSet {
3801 key: key.into(),
3802 value: Some(value.clone()),
3803 }),
3804 EventHint::Map {
3805 key: key.into(),
3806 value: Some(value.clone()),
3807 },
3808 &inner.doc,
3809 )
3810 }),
3811 }
3812 }
3813
3814 pub fn insert_with_txn(
3815 &self,
3816 txn: &mut Transaction,
3817 key: &str,
3818 value: LoroValue,
3819 ) -> LoroResult<()> {
3820 if let Some(_value) = value.as_container() {
3821 return Err(LoroError::ArgErr(
3822 INSERT_CONTAINER_VALUE_ARG_ERROR
3823 .to_string()
3824 .into_boxed_str(),
3825 ));
3826 }
3827
3828 if self.get(key).map(|x| x == value).unwrap_or(false) {
3829 return Ok(());
3831 }
3832
3833 let inner = self.inner.try_attached_state()?;
3834 txn.apply_local_op(
3835 inner.container_idx,
3836 crate::op::RawOpContent::Map(crate::container::map::MapSet {
3837 key: key.into(),
3838 value: Some(value.clone()),
3839 }),
3840 EventHint::Map {
3841 key: key.into(),
3842 value: Some(value.clone()),
3843 },
3844 &inner.doc,
3845 )
3846 }
3847
3848 pub fn insert_container<T: HandlerTrait>(&self, key: &str, handler: T) -> LoroResult<T> {
3849 match &self.inner {
3850 MaybeDetached::Detached(m) => {
3851 let mut m = m.lock().unwrap();
3852 let to_insert = handler.to_handler();
3853 m.value
3854 .insert(key.into(), ValueOrHandler::Handler(to_insert.clone()));
3855 Ok(handler)
3856 }
3857 MaybeDetached::Attached(a) => {
3858 a.with_txn(|txn| self.insert_container_with_txn(txn, key, handler))
3859 }
3860 }
3861 }
3862
3863 pub fn insert_container_with_txn<H: HandlerTrait>(
3864 &self,
3865 txn: &mut Transaction,
3866 key: &str,
3867 child: H,
3868 ) -> LoroResult<H> {
3869 let inner = self.inner.try_attached_state()?;
3870 let id = txn.next_id();
3871 let container_id = ContainerID::new_normal(id, child.kind());
3872 txn.apply_local_op(
3873 inner.container_idx,
3874 crate::op::RawOpContent::Map(crate::container::map::MapSet {
3875 key: key.into(),
3876 value: Some(LoroValue::Container(container_id.clone())),
3877 }),
3878 EventHint::Map {
3879 key: key.into(),
3880 value: Some(LoroValue::Container(container_id.clone())),
3881 },
3882 &inner.doc,
3883 )?;
3884
3885 child.attach(txn, inner, container_id)
3886 }
3887
3888 pub fn delete(&self, key: &str) -> LoroResult<()> {
3889 match &self.inner {
3890 MaybeDetached::Detached(m) => {
3891 let mut m = m.lock().unwrap();
3892 m.value.remove(key);
3893 Ok(())
3894 }
3895 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, key)),
3896 }
3897 }
3898
3899 pub fn delete_with_txn(&self, txn: &mut Transaction, key: &str) -> LoroResult<()> {
3900 let inner = self.inner.try_attached_state()?;
3901 txn.apply_local_op(
3902 inner.container_idx,
3903 crate::op::RawOpContent::Map(crate::container::map::MapSet {
3904 key: key.into(),
3905 value: None,
3906 }),
3907 EventHint::Map {
3908 key: key.into(),
3909 value: None,
3910 },
3911 &inner.doc,
3912 )
3913 }
3914
3915 pub fn for_each<I>(&self, mut f: I)
3916 where
3917 I: FnMut(&str, ValueOrHandler),
3918 {
3919 match &self.inner {
3920 MaybeDetached::Detached(m) => {
3921 let m = m.lock().unwrap();
3922 for (k, v) in m.value.iter() {
3923 f(k, v.clone());
3924 }
3925 }
3926 MaybeDetached::Attached(inner) => {
3927 let mut temp = vec![];
3928 inner.with_state(|state| {
3929 let a = state.as_map_state().unwrap();
3930 for (k, v) in a.iter() {
3931 if let Some(v) = &v.value {
3932 match v {
3933 LoroValue::Container(c) => {
3934 temp.push((
3935 k.to_string(),
3936 ValueOrHandler::Handler(create_handler(inner, c.clone())),
3937 ));
3938 }
3939 value => {
3940 temp.push((k.to_string(), ValueOrHandler::Value(value.clone())))
3941 }
3942 }
3943 }
3944 }
3945 });
3946
3947 for (k, v) in temp.into_iter() {
3948 f(&k, v.clone());
3949 }
3950 }
3951 }
3952 }
3953
3954 pub fn get_child_handler(&self, key: &str) -> LoroResult<Handler> {
3955 match &self.inner {
3956 MaybeDetached::Detached(m) => {
3957 let m = m.lock().unwrap();
3958 let value = m.value.get(key).unwrap();
3959 match value {
3960 ValueOrHandler::Value(v) => Err(LoroError::ArgErr(
3961 format!("Expected Handler but found {:?}", v).into_boxed_str(),
3962 )),
3963 ValueOrHandler::Handler(h) => Ok(h.clone()),
3964 }
3965 }
3966 MaybeDetached::Attached(inner) => {
3967 let container_id = inner.with_state(|state| {
3968 state
3969 .as_map_state()
3970 .as_ref()
3971 .unwrap()
3972 .get(key)
3973 .unwrap()
3974 .as_container()
3975 .unwrap()
3976 .clone()
3977 });
3978 Ok(create_handler(inner, container_id))
3979 }
3980 }
3981 }
3982
3983 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
3984 match &self.inner {
3985 MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
3986 method: "get_deep_value_with_id",
3987 }),
3988 MaybeDetached::Attached(inner) => Ok(inner.with_doc_state(|state| {
3989 state.get_container_deep_value_with_id(inner.container_idx, None)
3990 })),
3991 }
3992 }
3993
3994 pub fn get(&self, key: &str) -> Option<LoroValue> {
3995 match &self.inner {
3996 MaybeDetached::Detached(m) => {
3997 let m = m.lock().unwrap();
3998 m.value.get(key).map(|v| v.to_value())
3999 }
4000 MaybeDetached::Attached(inner) => {
4001 inner.with_state(|state| state.as_map_state().unwrap().get(key).cloned())
4002 }
4003 }
4004 }
4005
4006 pub fn get_(&self, key: &str) -> Option<ValueOrHandler> {
4008 match &self.inner {
4009 MaybeDetached::Detached(m) => {
4010 let m = m.lock().unwrap();
4011 m.value.get(key).cloned()
4012 }
4013 MaybeDetached::Attached(inner) => {
4014 let value =
4015 inner.with_state(|state| state.as_map_state().unwrap().get(key).cloned());
4016 match value {
4017 Some(LoroValue::Container(container_id)) => Some(ValueOrHandler::Handler(
4018 create_handler(inner, container_id.clone()),
4019 )),
4020 Some(value) => Some(ValueOrHandler::Value(value.clone())),
4021 None => None,
4022 }
4023 }
4024 }
4025 }
4026
4027 pub fn get_or_create_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4028 if let Some(ans) = self.get_(key) {
4029 if let ValueOrHandler::Handler(h) = ans {
4030 let kind = h.kind();
4031 return C::from_handler(h).ok_or_else(move || {
4032 LoroError::ArgErr(
4033 format!("Expected value type {} but found {:?}", child.kind(), kind)
4034 .into_boxed_str(),
4035 )
4036 });
4037 } else if let ValueOrHandler::Value(LoroValue::Null) = ans {
4038 } else {
4040 return Err(LoroError::ArgErr(
4041 format!("Expected value type {} but found {:?}", child.kind(), ans)
4042 .into_boxed_str(),
4043 ));
4044 }
4045 }
4046
4047 self.insert_container(key, child)
4048 }
4049
4050 pub fn contains_key(&self, key: &str) -> bool {
4051 self.get(key).is_some()
4052 }
4053
4054 pub fn len(&self) -> usize {
4055 match &self.inner {
4056 MaybeDetached::Detached(m) => m.lock().unwrap().value.len(),
4057 MaybeDetached::Attached(a) => a.with_state(|state| state.as_map_state().unwrap().len()),
4058 }
4059 }
4060
4061 pub fn is_empty(&self) -> bool {
4062 self.len() == 0
4063 }
4064
4065 pub fn is_deleted(&self) -> bool {
4066 match &self.inner {
4067 MaybeDetached::Detached(_) => false,
4068 MaybeDetached::Attached(a) => a.is_deleted(),
4069 }
4070 }
4071
4072 pub fn clear(&self) -> LoroResult<()> {
4073 match &self.inner {
4074 MaybeDetached::Detached(m) => {
4075 let mut m = m.lock().unwrap();
4076 m.value.clear();
4077 Ok(())
4078 }
4079 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4080 }
4081 }
4082
4083 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4084 let keys: Vec<InternalString> = self.inner.try_attached_state()?.with_state(|state| {
4085 state
4086 .as_map_state()
4087 .unwrap()
4088 .iter()
4089 .map(|(k, _)| k.clone())
4090 .collect()
4091 });
4092
4093 for key in keys {
4094 self.delete_with_txn(txn, &key)?;
4095 }
4096
4097 Ok(())
4098 }
4099
4100 pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
4101 let mut keys: Vec<InternalString> = Vec::with_capacity(self.len());
4102 match &self.inner {
4103 MaybeDetached::Detached(m) => {
4104 let m = m.lock().unwrap();
4105 keys = m.value.keys().map(|x| x.as_str().into()).collect();
4106 }
4107 MaybeDetached::Attached(a) => {
4108 a.with_state(|state| {
4109 for (k, v) in state.as_map_state().unwrap().iter() {
4110 if v.value.is_some() {
4111 keys.push(k.clone());
4112 }
4113 }
4114 });
4115 }
4116 }
4117
4118 keys.into_iter()
4119 }
4120
4121 pub fn values(&self) -> impl Iterator<Item = ValueOrHandler> + '_ {
4122 let mut values: Vec<ValueOrHandler> = Vec::with_capacity(self.len());
4123 match &self.inner {
4124 MaybeDetached::Detached(m) => {
4125 let m = m.lock().unwrap();
4126 values = m.value.values().cloned().collect();
4127 }
4128 MaybeDetached::Attached(a) => {
4129 a.with_state(|state| {
4130 for (_, v) in state.as_map_state().unwrap().iter() {
4131 let value = match &v.value {
4132 Some(LoroValue::Container(container_id)) => {
4133 ValueOrHandler::Handler(create_handler(a, container_id.clone()))
4134 }
4135 Some(value) => ValueOrHandler::Value(value.clone()),
4136 None => continue,
4137 };
4138 values.push(value);
4139 }
4140 });
4141 }
4142 }
4143
4144 values.into_iter()
4145 }
4146
4147 pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
4148 match &self.inner {
4149 MaybeDetached::Detached(_) => None,
4150 MaybeDetached::Attached(a) => a.with_state(|state| {
4151 let m = state.as_map_state().unwrap();
4152 m.get_last_edit_peer(key)
4153 }),
4154 }
4155 }
4156}
4157
4158fn with_txn<R>(doc: &LoroDoc, f: impl FnOnce(&mut Transaction) -> LoroResult<R>) -> LoroResult<R> {
4159 let txn = &doc.txn;
4160 let mut txn = txn.lock().unwrap();
4161 loop {
4162 if let Some(txn) = &mut *txn {
4163 return f(txn);
4164 } else if cfg!(target_arch = "wasm32") || !doc.can_edit() {
4165 return Err(LoroError::AutoCommitNotStarted);
4166 } else {
4167 drop(txn);
4168 #[cfg(loom)]
4169 loom::thread::yield_now();
4170 doc.start_auto_commit();
4171 txn = doc.txn.lock().unwrap();
4172 }
4173 }
4174}
4175
4176#[cfg(feature = "counter")]
4177pub mod counter {
4178
4179 use loro_common::LoroResult;
4180
4181 use crate::{
4182 txn::{EventHint, Transaction},
4183 HandlerTrait,
4184 };
4185
4186 use super::{create_handler, Handler, MaybeDetached};
4187
4188 #[derive(Clone)]
4189 pub struct CounterHandler {
4190 pub(super) inner: MaybeDetached<f64>,
4191 }
4192
4193 impl CounterHandler {
4194 pub fn new_detached() -> Self {
4195 Self {
4196 inner: MaybeDetached::new_detached(0.),
4197 }
4198 }
4199
4200 pub fn increment(&self, n: f64) -> LoroResult<()> {
4201 match &self.inner {
4202 MaybeDetached::Detached(d) => {
4203 let d = &mut d.lock().unwrap().value;
4204 *d += n;
4205 Ok(())
4206 }
4207 MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, n)),
4208 }
4209 }
4210
4211 pub fn decrement(&self, n: f64) -> LoroResult<()> {
4212 match &self.inner {
4213 MaybeDetached::Detached(d) => {
4214 let d = &mut d.lock().unwrap().value;
4215 *d -= n;
4216 Ok(())
4217 }
4218 MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, -n)),
4219 }
4220 }
4221
4222 fn increment_with_txn(&self, txn: &mut Transaction, n: f64) -> LoroResult<()> {
4223 let inner = self.inner.try_attached_state()?;
4224 txn.apply_local_op(
4225 inner.container_idx,
4226 crate::op::RawOpContent::Counter(n),
4227 EventHint::Counter(n),
4228 &inner.doc,
4229 )
4230 }
4231
4232 pub fn is_deleted(&self) -> bool {
4233 match &self.inner {
4234 MaybeDetached::Detached(_) => false,
4235 MaybeDetached::Attached(a) => a.is_deleted(),
4236 }
4237 }
4238
4239 pub fn clear(&self) -> LoroResult<()> {
4240 self.decrement(self.get_value().into_double().unwrap())
4241 }
4242 }
4243
4244 impl std::fmt::Debug for CounterHandler {
4245 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4246 match &self.inner {
4247 MaybeDetached::Detached(_) => write!(f, "CounterHandler Detached"),
4248 MaybeDetached::Attached(a) => write!(f, "CounterHandler {}", a.id),
4249 }
4250 }
4251 }
4252
4253 impl HandlerTrait for CounterHandler {
4254 fn is_attached(&self) -> bool {
4255 matches!(&self.inner, MaybeDetached::Attached(..))
4256 }
4257
4258 fn attached_handler(&self) -> Option<&crate::BasicHandler> {
4259 self.inner.attached_handler()
4260 }
4261
4262 fn get_value(&self) -> loro_common::LoroValue {
4263 match &self.inner {
4264 MaybeDetached::Detached(t) => {
4265 let t = t.lock().unwrap();
4266 t.value.into()
4267 }
4268 MaybeDetached::Attached(a) => a.get_value(),
4269 }
4270 }
4271
4272 fn get_deep_value(&self) -> loro_common::LoroValue {
4273 self.get_value()
4274 }
4275
4276 fn kind(&self) -> loro_common::ContainerType {
4277 loro_common::ContainerType::Counter
4278 }
4279
4280 fn to_handler(&self) -> super::Handler {
4281 Handler::Counter(self.clone())
4282 }
4283
4284 fn from_handler(h: super::Handler) -> Option<Self> {
4285 match h {
4286 Handler::Counter(x) => Some(x),
4287 _ => None,
4288 }
4289 }
4290
4291 fn attach(
4292 &self,
4293 txn: &mut crate::txn::Transaction,
4294 parent: &crate::BasicHandler,
4295 self_id: loro_common::ContainerID,
4296 ) -> loro_common::LoroResult<Self> {
4297 match &self.inner {
4298 MaybeDetached::Detached(v) => {
4299 let mut v = v.lock().unwrap();
4300 let inner = create_handler(parent, self_id);
4301 let c = inner.into_counter().unwrap();
4302
4303 c.increment_with_txn(txn, v.value)?;
4304
4305 v.attached = c.attached_handler().cloned();
4306 Ok(c)
4307 }
4308 MaybeDetached::Attached(a) => {
4309 let new_inner = create_handler(a, self_id);
4310 let ans = new_inner.into_counter().unwrap();
4311 let delta = *self.get_value().as_double().unwrap();
4312 ans.increment_with_txn(txn, delta)?;
4313 Ok(ans)
4314 }
4315 }
4316 }
4317
4318 fn get_attached(&self) -> Option<Self> {
4319 match &self.inner {
4320 MaybeDetached::Attached(a) => Some(Self {
4321 inner: MaybeDetached::Attached(a.clone()),
4322 }),
4323 MaybeDetached::Detached(_) => None,
4324 }
4325 }
4326
4327 fn doc(&self) -> Option<crate::LoroDoc> {
4328 match &self.inner {
4329 MaybeDetached::Detached(_) => None,
4330 MaybeDetached::Attached(a) => Some(a.doc()),
4331 }
4332 }
4333 }
4334}
4335
4336#[cfg(test)]
4337mod test {
4338
4339 use super::{HandlerTrait, TextDelta};
4340 use crate::cursor::PosType;
4341 use crate::loro::ExportMode;
4342 use crate::state::TreeParentId;
4343 use crate::version::Frontiers;
4344 use crate::LoroDoc;
4345 use crate::{fx_map, ToJson};
4346 use loro_common::{LoroValue, ID};
4347 use serde_json::json;
4348
4349 #[test]
4350 fn richtext_handler() {
4351 let loro = LoroDoc::new();
4352 loro.set_peer_id(1).unwrap();
4353 let loro2 = LoroDoc::new();
4354 loro2.set_peer_id(2).unwrap();
4355
4356 let mut txn = loro.txn().unwrap();
4357 let text = txn.get_text("hello");
4358 text.insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
4359 .unwrap();
4360 txn.commit().unwrap();
4361 let exported = loro.export(ExportMode::all_updates()).unwrap();
4362
4363 loro2.import(&exported).unwrap();
4364 let mut txn = loro2.txn().unwrap();
4365 let text = txn.get_text("hello");
4366 assert_eq!(&**text.get_value().as_string().unwrap(), "hello");
4367 text.insert_with_txn(&mut txn, 5, " world", PosType::Unicode)
4368 .unwrap();
4369 assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
4370 txn.commit().unwrap();
4371
4372 loro.import(&loro2.export(ExportMode::all_updates()).unwrap())
4373 .unwrap();
4374 let txn = loro.txn().unwrap();
4375 let text = txn.get_text("hello");
4376 assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
4377 txn.commit().unwrap();
4378
4379 loro.checkout(&Frontiers::from_id(ID::new(2, 1))).unwrap();
4381 assert_eq!(&**text.get_value().as_string().unwrap(), "hello w");
4382 }
4383
4384 #[test]
4385 fn richtext_handler_concurrent() {
4386 let loro = LoroDoc::new();
4387 let mut txn = loro.txn().unwrap();
4388 let handler = loro.get_text("richtext");
4389 handler
4390 .insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
4391 .unwrap();
4392 txn.commit().unwrap();
4393 for i in 0..100 {
4394 let new_loro = LoroDoc::new();
4395 new_loro
4396 .import(&loro.export(ExportMode::all_updates()).unwrap())
4397 .unwrap();
4398 let mut txn = new_loro.txn().unwrap();
4399 let handler = new_loro.get_text("richtext");
4400 handler
4401 .insert_with_txn(&mut txn, i % 5, &i.to_string(), PosType::Unicode)
4402 .unwrap();
4403 txn.commit().unwrap();
4404 loro.import(
4405 &new_loro
4406 .export(ExportMode::updates(&loro.oplog_vv()))
4407 .unwrap(),
4408 )
4409 .unwrap();
4410 }
4411 }
4412
4413 #[test]
4414 fn richtext_handler_mark() {
4415 let loro = LoroDoc::new_auto_commit();
4416 let handler = loro.get_text("richtext");
4417 handler.insert(0, "hello world", PosType::Unicode).unwrap();
4418 handler
4419 .mark(0, 5, "bold", true.into(), PosType::Event)
4420 .unwrap();
4421 loro.commit_then_renew();
4422
4423 let value = handler.get_richtext_value();
4425 assert_eq!(value[0]["insert"], "hello".into());
4426 let meta = value[0]["attributes"].as_map().unwrap();
4427 assert_eq!(meta.len(), 1);
4428 meta.get("bold").unwrap();
4429
4430 let loro2 = LoroDoc::new_auto_commit();
4431 loro2
4432 .import(&loro.export(ExportMode::all_updates()).unwrap())
4433 .unwrap();
4434 let handler2 = loro2.get_text("richtext");
4435 assert_eq!(&**handler2.get_value().as_string().unwrap(), "hello world");
4436
4437 let value = handler2.get_richtext_value();
4439 assert_eq!(value[0]["insert"], "hello".into());
4440 let meta = value[0]["attributes"].as_map().unwrap();
4441 assert_eq!(meta.len(), 1);
4442 meta.get("bold").unwrap();
4443
4444 {
4446 handler2.insert(5, " new", PosType::Unicode).unwrap();
4447 let value = handler2.get_richtext_value();
4448 assert_eq!(
4449 value.to_json_value(),
4450 serde_json::json!([
4451 {"insert": "hello new", "attributes": {"bold": true}},
4452 {"insert": " world"}
4453 ])
4454 );
4455 }
4456 }
4457
4458 #[test]
4459 fn richtext_snapshot() {
4460 let loro = LoroDoc::new();
4461 let mut txn = loro.txn().unwrap();
4462 let handler = loro.get_text("richtext");
4463 handler
4464 .insert_with_txn(&mut txn, 0, "hello world", PosType::Unicode)
4465 .unwrap();
4466 handler
4467 .mark_with_txn(&mut txn, 0, 5, "bold", true.into(), PosType::Event)
4468 .unwrap();
4469 txn.commit().unwrap();
4470
4471 let loro2 = LoroDoc::new();
4472 loro2
4473 .import(&loro.export(ExportMode::snapshot()).unwrap())
4474 .unwrap();
4475 let handler2 = loro2.get_text("richtext");
4476 assert_eq!(
4477 handler2.get_richtext_value().to_json_value(),
4478 serde_json::json!([
4479 {"insert": "hello", "attributes": {"bold": true}},
4480 {"insert": " world"}
4481 ])
4482 );
4483 }
4484
4485 #[test]
4486 fn tree_meta() {
4487 let loro = LoroDoc::new_auto_commit();
4488 loro.set_peer_id(1).unwrap();
4489 let tree = loro.get_tree("root");
4490 let id = tree.create(TreeParentId::Root).unwrap();
4491 let meta = tree.get_meta(id).unwrap();
4492 meta.insert("a", 123).unwrap();
4493 loro.commit_then_renew();
4494 let meta = tree.get_meta(id).unwrap();
4495 assert_eq!(meta.get("a").unwrap(), 123.into());
4496 assert_eq!(
4497 json!([{"parent":null,"meta":{"a":123},"id":"0@1","index":0,"children":[],"fractional_index":"80"}]),
4498 tree.get_deep_value().to_json_value()
4499 );
4500 let bytes = loro.export(ExportMode::snapshot()).unwrap();
4501 let loro2 = LoroDoc::new();
4502 loro2.import(&bytes).unwrap();
4503 }
4504
4505 #[test]
4506 fn tree_meta_event() {
4507 use std::sync::Arc;
4508 let loro = LoroDoc::new_auto_commit();
4509 let tree = loro.get_tree("root");
4510 let text = loro.get_text("text");
4511
4512 let id = tree.create(TreeParentId::Root).unwrap();
4513 let meta = tree.get_meta(id).unwrap();
4514 meta.insert("a", 1).unwrap();
4515 text.insert(0, "abc", PosType::Unicode).unwrap();
4516 let _id2 = tree.create(TreeParentId::Root).unwrap();
4517 meta.insert("b", 2).unwrap();
4518
4519 let loro2 = LoroDoc::new_auto_commit();
4520 let _g = loro2.subscribe_root(Arc::new(|e| {
4521 println!("{} {:?} ", e.event_meta.by, e.event_meta.diff)
4522 }));
4523 loro2
4524 .import(&loro.export(ExportMode::all_updates()).unwrap())
4525 .unwrap();
4526 assert_eq!(loro.get_deep_value(), loro2.get_deep_value());
4527 }
4528
4529 #[test]
4530 fn richtext_apply_delta() {
4531 let loro = LoroDoc::new_auto_commit();
4532 let text = loro.get_text("text");
4533 text.apply_delta(&[TextDelta::Insert {
4534 insert: "Hello World!".into(),
4535 attributes: None,
4536 }])
4537 .unwrap();
4538 dbg!(text.get_richtext_value());
4539 text.apply_delta(&[
4540 TextDelta::Retain {
4541 retain: 6,
4542 attributes: Some(fx_map!("italic".into() => loro_common::LoroValue::Bool(true))),
4543 },
4544 TextDelta::Insert {
4545 insert: "New ".into(),
4546 attributes: Some(fx_map!("bold".into() => loro_common::LoroValue::Bool(true))),
4547 },
4548 ])
4549 .unwrap();
4550 dbg!(text.get_richtext_value());
4551 loro.commit_then_renew();
4552 assert_eq!(
4553 text.get_richtext_value().to_json_value(),
4554 json!([
4555 {"insert": "Hello ", "attributes": {"italic": true}},
4556 {"insert": "New ", "attributes": {"bold": true}},
4557 {"insert": "World!"}
4558
4559 ])
4560 )
4561 }
4562
4563 #[test]
4564 fn richtext_apply_delta_marks_without_growth() {
4565 let loro = LoroDoc::new_auto_commit();
4566 let text = loro.get_text("text");
4567 text.insert(0, "abc", PosType::Unicode).unwrap();
4568
4569 text.apply_delta(&[TextDelta::Retain {
4570 retain: 3,
4571 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
4572 }])
4573 .unwrap();
4574 loro.commit_then_renew();
4575
4576 assert_eq!(text.to_string(), "abc");
4577 assert_eq!(
4578 text.get_richtext_value().to_json_value(),
4579 json!([{"insert": "abc", "attributes": {"bold": true}}])
4580 );
4581 }
4582
4583 #[test]
4584 fn richtext_apply_delta_grows_for_mark_gap() {
4585 let loro = LoroDoc::new_auto_commit();
4586 let text = loro.get_text("text");
4587
4588 text.apply_delta(&[TextDelta::Retain {
4589 retain: 1,
4590 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
4591 }])
4592 .unwrap();
4593 loro.commit_then_renew();
4594
4595 assert_eq!(text.to_string(), "\n");
4596 assert_eq!(
4597 text.get_richtext_value().to_json_value(),
4598 json!([{"insert": "\n", "attributes": {"bold": true}}])
4599 );
4600 }
4601
4602 #[test]
4603 fn richtext_apply_delta_ignores_empty_inserts() {
4604 let loro = LoroDoc::new_auto_commit();
4605 let text = loro.get_text("text");
4606 text.insert(0, "seed", PosType::Unicode).unwrap();
4607
4608 text.apply_delta(&[TextDelta::Insert {
4609 insert: "".into(),
4610 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
4611 }])
4612 .unwrap();
4613 loro.commit_then_renew();
4614
4615 assert_eq!(text.to_string(), "seed");
4616 assert_eq!(
4617 text.get_richtext_value().to_json_value(),
4618 json!([{"insert": "seed"}])
4619 );
4620 }
4621}