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