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(
1858 pos,
1859 len,
1860 self.len(pos_type),
1861 || format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1862 )?;
1863 let x = self.slice(pos, end, pos_type)?;
1864 self.delete(pos, len, pos_type)?;
1865 self.insert(pos, s, pos_type)?;
1866 Ok(x)
1867 }
1868
1869 pub fn splice_utf8(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1870 self.delete_utf8(pos, len)?;
1872 self.insert_utf8(pos, s)?;
1873 Ok(())
1874 }
1875
1876 pub fn splice_utf16(&self, pos: usize, len: usize, s: &str) -> LoroResult<()> {
1877 self.delete(pos, len, PosType::Utf16)?;
1878 self.insert(pos, s, PosType::Utf16)?;
1879 Ok(())
1880 }
1881
1882 pub fn insert(&self, pos: usize, s: &str, pos_type: PosType) -> LoroResult<()> {
1886 match &self.inner {
1887 MaybeDetached::Detached(t) => {
1888 let len = self.len(pos_type);
1889 if pos > len {
1890 return Err(LoroError::OutOfBound {
1891 pos,
1892 len,
1893 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1894 });
1895 }
1896 self.validate_text_boundary(pos, pos_type)?;
1897
1898 let mut t = t.lock();
1899 let (index, _) = t
1900 .value
1901 .get_entity_index_for_text_insert(pos, pos_type)
1902 .unwrap();
1903 t.value.insert_at_entity_index(
1904 index,
1905 BytesSlice::from_bytes(s.as_bytes()),
1906 IdFull::NONE_ID,
1907 );
1908 Ok(())
1909 }
1910 MaybeDetached::Attached(a) => {
1911 if s.is_empty() {
1912 let len = self.len(pos_type);
1913 if pos > len {
1914 return Err(LoroError::OutOfBound {
1915 pos,
1916 len,
1917 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1918 });
1919 }
1920 self.validate_text_boundary(pos, pos_type)?;
1921 return Ok(());
1922 }
1923
1924 a.with_txn(|txn| self.insert_with_txn(txn, pos, s, pos_type))
1925 }
1926 }
1927 }
1928
1929 pub fn insert_utf8(&self, pos: usize, s: &str) -> LoroResult<()> {
1930 self.insert(pos, s, PosType::Bytes)
1931 }
1932
1933 pub fn insert_utf16(&self, pos: usize, s: &str) -> LoroResult<()> {
1934 self.insert(pos, s, PosType::Utf16)
1935 }
1936
1937 pub fn insert_unicode(&self, pos: usize, s: &str) -> LoroResult<()> {
1938 self.insert(pos, s, PosType::Unicode)
1939 }
1940
1941 pub fn insert_with_txn(
1943 &self,
1944 txn: &mut Transaction,
1945 pos: usize,
1946 s: &str,
1947 pos_type: PosType,
1948 ) -> LoroResult<()> {
1949 self.insert_with_txn_and_attr(txn, pos, s, None, pos_type)?;
1950 Ok(())
1951 }
1952
1953 pub fn insert_with_txn_utf8(
1954 &self,
1955 txn: &mut Transaction,
1956 pos: usize,
1957 s: &str,
1958 ) -> LoroResult<()> {
1959 self.insert_with_txn(txn, pos, s, PosType::Bytes)
1960 }
1961
1962 pub fn delete(&self, pos: usize, len: usize, pos_type: PosType) -> LoroResult<()> {
1966 if len == 0 {
1967 return Ok(());
1968 }
1969
1970 let text_len = self.len(pos_type);
1971 let end = checked_range_end(
1972 pos,
1973 len,
1974 text_len,
1975 || format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
1976 )?;
1977 self.validate_text_boundary(pos, pos_type)?;
1978 self.validate_text_boundary(end, pos_type)?;
1979
1980 match &self.inner {
1981 MaybeDetached::Detached(t) => {
1982 let mut t = t.lock();
1983 let ranges = t.value.get_text_entity_ranges(pos, len, pos_type)?;
1984 for range in ranges.iter().rev() {
1985 t.value
1986 .drain_by_entity_index(range.entity_start, range.entity_len(), None);
1987 }
1988 Ok(())
1989 }
1990 MaybeDetached::Attached(a) => {
1991 a.with_txn(|txn| self.delete_with_txn(txn, pos, len, pos_type))
1992 }
1993 }
1994 }
1995
1996 pub fn delete_utf8(&self, pos: usize, len: usize) -> LoroResult<()> {
1997 self.delete(pos, len, PosType::Bytes)
1998 }
1999
2000 pub fn delete_utf16(&self, pos: usize, len: usize) -> LoroResult<()> {
2001 self.delete(pos, len, PosType::Utf16)
2002 }
2003
2004 pub fn delete_unicode(&self, pos: usize, len: usize) -> LoroResult<()> {
2005 self.delete(pos, len, PosType::Unicode)
2006 }
2007
2008 fn insert_with_txn_and_attr(
2011 &self,
2012 txn: &mut Transaction,
2013 pos: usize,
2014 s: &str,
2015 attr: Option<&FxHashMap<String, LoroValue>>,
2016 pos_type: PosType,
2017 ) -> Result<Vec<(InternalString, LoroValue)>, LoroError> {
2018 if s.is_empty() {
2019 return Ok(Vec::new());
2020 }
2021
2022 #[cfg(not(feature = "wasm"))]
2029 if attr.is_none() && pos_type == PosType::Unicode {
2030 let inner = self.inner.try_attached_state()?;
2031 let fast = inner.with_state(|state| {
2032 let rt = state.as_richtext_state_mut().unwrap();
2033 if rt.has_styles() {
2034 return Ok(false);
2035 }
2036 let len = rt.len_unicode();
2037 if pos > len {
2038 return Err(LoroError::OutOfBound {
2039 pos,
2040 len,
2041 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2042 });
2043 }
2044 Ok(true)
2045 })?;
2046 if fast {
2047 let unicode_len = s.chars().count();
2048 txn.apply_local_op(
2049 inner.container_idx,
2050 crate::op::RawOpContent::List(
2051 crate::container::list::list_op::ListOp::Insert {
2052 slice: ListSlice::RawStr {
2053 str: Cow::Borrowed(s),
2054 unicode_len,
2055 },
2056 pos,
2058 },
2059 ),
2060 EventHint::InsertText {
2061 pos: pos as u32,
2063 styles: StyleMeta::empty(),
2064 unicode_len: unicode_len as u32,
2065 event_len: unicode_len as u32,
2066 },
2067 &inner.doc,
2068 )?;
2069 return Ok(Vec::new());
2070 }
2071 }
2072
2073 match pos_type {
2074 PosType::Event => {
2075 if pos > self.len_event() {
2076 return Err(LoroError::OutOfBound {
2077 pos,
2078 len: self.len_event(),
2079 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2080 });
2081 }
2082 }
2083 PosType::Bytes => {
2084 if pos > self.len_utf8() {
2085 return Err(LoroError::OutOfBound {
2086 pos,
2087 len: self.len_utf8(),
2088 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2089 });
2090 }
2091 }
2092 PosType::Unicode => {
2093 if pos > self.len_unicode() {
2094 return Err(LoroError::OutOfBound {
2095 pos,
2096 len: self.len_unicode(),
2097 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2098 });
2099 }
2100 }
2101 PosType::Entity => {}
2102 PosType::Utf16 => {
2103 if pos > self.len_utf16() {
2104 return Err(LoroError::OutOfBound {
2105 pos,
2106 len: self.len_utf16(),
2107 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2108 });
2109 }
2110 }
2111 }
2112 self.validate_text_boundary(pos, pos_type)?;
2113
2114 let inner = self.inner.try_attached_state()?;
2115 let (entity_index, event_index, styles) = inner.with_state(|state| {
2116 let richtext_state = state.as_richtext_state_mut().unwrap();
2117 let ret = richtext_state.get_entity_index_for_text_insert(pos, pos_type);
2118 let (entity_index, cursor) = match ret {
2119 Err(_) => match pos_type {
2120 PosType::Bytes => {
2121 return (
2122 Err(LoroError::UTF8InUnicodeCodePoint { pos }),
2123 0,
2124 StyleMeta::empty(),
2125 );
2126 }
2127 PosType::Utf16 | PosType::Event => {
2128 return (
2129 Err(LoroError::UTF16InUnicodeCodePoint { pos }),
2130 0,
2131 StyleMeta::empty(),
2132 );
2133 }
2134 _ => unreachable!(),
2135 },
2136 Ok(x) => x,
2137 };
2138 let event_index = if let Some(cursor) = cursor {
2139 if pos_type == PosType::Event {
2140 debug_assert_eq!(
2141 richtext_state.get_event_index_by_cursor(cursor),
2142 pos,
2143 "pos={} cursor={:?} state={:#?}",
2144 pos,
2145 cursor,
2146 &richtext_state
2147 );
2148 pos
2149 } else {
2150 richtext_state.get_event_index_by_cursor(cursor)
2151 }
2152 } else {
2153 assert_eq!(entity_index, 0);
2154 0
2155 };
2156 let styles = richtext_state.get_styles_at_entity_index(entity_index);
2157 (Ok(entity_index), event_index, styles)
2158 });
2159
2160 let entity_index = match entity_index {
2161 Err(x) => return Err(x),
2162 _ => entity_index.unwrap(),
2163 };
2164
2165 let mut override_styles = Vec::new();
2166 if let Some(attr) = attr {
2167 let map: FxHashMap<_, _> = styles.iter().map(|x| (x.0.clone(), x.1.data)).collect();
2169 for (key, style) in map.iter() {
2170 match attr.get(key.deref()) {
2171 Some(v) if v == style => {}
2172 new_style_value => {
2173 let new_style_value = new_style_value.cloned().unwrap_or(LoroValue::Null);
2175 override_styles.push((key.clone(), new_style_value));
2176 }
2177 }
2178 }
2179
2180 for (key, style) in attr.iter() {
2181 let key = key.as_str().into();
2182 if !map.contains_key(&key) {
2183 override_styles.push((key, style.clone()));
2184 }
2185 }
2186 }
2187
2188 let unicode_len = s.chars().count();
2189 let event_len = if cfg!(feature = "wasm") {
2190 count_utf16_len(s.as_bytes())
2191 } else {
2192 unicode_len
2193 };
2194
2195 txn.apply_local_op(
2196 inner.container_idx,
2197 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
2198 slice: ListSlice::RawStr {
2199 str: Cow::Borrowed(s),
2200 unicode_len,
2201 },
2202 pos: entity_index,
2203 }),
2204 EventHint::InsertText {
2205 pos: event_index as u32,
2206 styles,
2207 unicode_len: unicode_len as u32,
2208 event_len: event_len as u32,
2209 },
2210 &inner.doc,
2211 )?;
2212
2213 Ok(override_styles)
2214 }
2215
2216 pub fn delete_with_txn(
2218 &self,
2219 txn: &mut Transaction,
2220 pos: usize,
2221 len: usize,
2222 pos_type: PosType,
2223 ) -> LoroResult<()> {
2224 self.delete_with_txn_inline(txn, pos, len, pos_type)
2225 }
2226
2227 fn delete_with_txn_inline(
2228 &self,
2229 txn: &mut Transaction,
2230 pos: usize,
2231 len: usize,
2232 pos_type: PosType,
2233 ) -> LoroResult<()> {
2234 if len == 0 {
2235 return Ok(());
2236 }
2237
2238 let text_len = self.len(pos_type);
2239 let end = checked_range_end(
2240 pos,
2241 len,
2242 text_len,
2243 || format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2244 )
2245 .inspect_err(|_| error!("pos={} len={} len={}", pos, len, text_len))?;
2246 self.validate_text_boundary(pos, pos_type)?;
2247 self.validate_text_boundary(end, pos_type)?;
2248
2249 let inner = self.inner.try_attached_state()?;
2250 let s = tracing::span!(tracing::Level::INFO, "delete", "pos={} len={}", pos, len);
2251 let _e = s.enter();
2252 let mut event_pos = 0;
2253 let mut event_len = 0;
2254 let ranges = inner.with_state(|state| {
2255 let richtext_state = state.as_richtext_state_mut().unwrap();
2256 let fast = cfg!(not(feature = "wasm"))
2260 && pos_type == PosType::Unicode
2261 && !richtext_state.has_styles();
2262 if fast {
2263 event_pos = pos;
2264 event_len = len;
2265 } else {
2266 event_pos = richtext_state.index_to_event_index(pos, pos_type);
2267 let event_end = richtext_state.index_to_event_index(end, pos_type);
2268 event_len = event_end - event_pos;
2269 }
2270
2271 richtext_state.get_text_entity_ranges_in_event_index_range(event_pos, event_len)
2272 })?;
2273
2274 let pos = event_pos as isize;
2276 let len = event_len as isize;
2277 let mut event_end = pos + len;
2278 for range in ranges.iter().rev() {
2279 let event_start = event_end - range.event_len as isize;
2280 txn.apply_local_op(
2281 inner.container_idx,
2282 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
2283 range.id_start,
2284 range.entity_start as isize,
2285 range.entity_len() as isize,
2286 ))),
2287 EventHint::DeleteText {
2288 span: DeleteSpan {
2289 pos: event_start,
2290 signed_len: range.event_len as isize,
2291 },
2292 unicode_len: range.entity_len(),
2293 },
2294 &inner.doc,
2295 )?;
2296 event_end = event_start;
2297 }
2298
2299 Ok(())
2300 }
2301
2302 pub fn mark(
2306 &self,
2307 start: usize,
2308 end: usize,
2309 key: impl Into<InternalString>,
2310 value: LoroValue,
2311 pos_type: PosType,
2312 ) -> LoroResult<()> {
2313 match &self.inner {
2314 MaybeDetached::Detached(t) => {
2315 let mut g = t.lock();
2316 self.mark_for_detached(&mut g.value, key, &value, start, end, pos_type)
2317 }
2318 MaybeDetached::Attached(a) => {
2319 a.with_txn(|txn| self.mark_with_txn(txn, start, end, key, value, pos_type))
2320 }
2321 }
2322 }
2323
2324 fn mark_for_detached(
2325 &self,
2326 state: &mut RichtextState,
2327 key: impl Into<InternalString>,
2328 value: &LoroValue,
2329 start: usize,
2330 end: usize,
2331 pos_type: PosType,
2332 ) -> Result<(), LoroError> {
2333 let key: InternalString = key.into();
2334 let is_delete = matches!(value, &LoroValue::Null);
2335 if start >= end {
2336 return Err(loro_common::LoroError::ArgErr(
2337 "Start must be less than end".to_string().into_boxed_str(),
2338 ));
2339 }
2340 ensure_no_regular_container_value(value)?;
2341
2342 let len = state.len(pos_type);
2343 if end > len {
2344 return Err(LoroError::OutOfBound {
2345 pos: end,
2346 len,
2347 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2348 });
2349 }
2350 let (entity_range, styles) =
2351 state.get_entity_range_and_text_styles_at_range(start..end, pos_type);
2352 if let Some(styles) = styles {
2353 if styles.has_key_value(&key, value) {
2354 return Ok(());
2355 }
2356 }
2357
2358 let has_target_style =
2359 state.range_has_style_key(entity_range.clone(), &StyleKey::Key(key.clone()));
2360 let missing_style_key = is_delete && !has_target_style;
2361
2362 if missing_style_key {
2363 return Ok(());
2364 }
2365
2366 let style_op = Arc::new(StyleOp {
2367 lamport: 0,
2368 peer: 0,
2369 cnt: 0,
2370 key,
2371 value: value.clone(),
2372 info: if is_delete {
2374 TextStyleInfoFlag::BOLD.to_delete()
2375 } else {
2376 TextStyleInfoFlag::BOLD
2377 },
2378 });
2379 state.mark_with_entity_index(entity_range, style_op);
2380 Ok(())
2381 }
2382
2383 pub fn unmark(
2385 &self,
2386 start: usize,
2387 end: usize,
2388 key: impl Into<InternalString>,
2389 pos_type: PosType,
2390 ) -> LoroResult<()> {
2391 match &self.inner {
2392 MaybeDetached::Detached(t) => self.mark_for_detached(
2393 &mut t.lock().value,
2394 key,
2395 &LoroValue::Null,
2396 start,
2397 end,
2398 pos_type,
2399 ),
2400 MaybeDetached::Attached(a) => a.with_txn(|txn| {
2401 self.mark_with_txn(txn, start, end, key, LoroValue::Null, pos_type)
2402 }),
2403 }
2404 }
2405
2406 pub fn mark_with_txn(
2408 &self,
2409 txn: &mut Transaction,
2410 start: usize,
2411 end: usize,
2412 key: impl Into<InternalString>,
2413 value: LoroValue,
2414 pos_type: PosType,
2415 ) -> LoroResult<()> {
2416 if start >= end {
2417 return Err(loro_common::LoroError::ArgErr(
2418 "Start must be less than end".to_string().into_boxed_str(),
2419 ));
2420 }
2421 ensure_no_regular_container_value(&value)?;
2422
2423 let inner = self.inner.try_attached_state()?;
2424 let key: InternalString = key.into();
2425 let is_delete = matches!(&value, &LoroValue::Null);
2426
2427 let mut doc_state = inner.doc.state.lock();
2428 let len = doc_state.with_state_mut(inner.container_idx, |state| {
2429 state.as_richtext_state_mut().unwrap().len(pos_type)
2430 });
2431
2432 if end > len {
2433 return Err(LoroError::OutOfBound {
2434 pos: end,
2435 len,
2436 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
2437 });
2438 }
2439
2440 let (entity_range, skip, missing_style_key, event_start, event_end) = doc_state
2441 .with_state_mut(inner.container_idx, |state| {
2442 let state = state.as_richtext_state_mut().unwrap();
2443 let event_start = state.index_to_event_index(start, pos_type);
2444 let event_end = state.index_to_event_index(end, pos_type);
2445 let (entity_range, styles) =
2446 state.get_entity_range_and_styles_at_range(start..end, pos_type);
2447
2448 let skip = styles
2449 .as_ref()
2450 .map(|styles| styles.has_key_value(&key, &value))
2451 .unwrap_or(false);
2452 let has_target_style = state.has_style_key_in_entity_range(
2453 entity_range.clone(),
2454 &StyleKey::Key(key.clone()),
2455 );
2456 let missing_style_key = is_delete && !has_target_style;
2457
2458 (
2459 entity_range,
2460 skip,
2461 missing_style_key,
2462 event_start,
2463 event_end,
2464 )
2465 });
2466
2467 if skip || missing_style_key {
2468 return Ok(());
2469 }
2470
2471 let entity_start = entity_range.start;
2472 let entity_end = entity_range.end;
2473 let style_config = doc_state.config.text_style_config.read();
2474 let flag = if is_delete {
2475 style_config
2476 .get_style_flag_for_unmark(&key)
2477 .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2478 } else {
2479 style_config
2480 .get_style_flag(&key)
2481 .ok_or_else(|| LoroError::StyleConfigMissing(key.clone()))?
2482 };
2483
2484 drop(style_config);
2485 drop(doc_state);
2486 txn.apply_local_op(
2487 inner.container_idx,
2488 crate::op::RawOpContent::List(ListOp::StyleStart {
2489 start: entity_start as u32,
2490 end: entity_end as u32,
2491 key: key.clone(),
2492 value: value.clone(),
2493 info: flag,
2494 }),
2495 EventHint::Mark {
2496 start: event_start as u32,
2497 end: event_end as u32,
2498 style: crate::container::richtext::Style { key, data: value },
2499 },
2500 &inner.doc,
2501 )?;
2502
2503 txn.apply_local_op(
2504 inner.container_idx,
2505 crate::op::RawOpContent::List(ListOp::StyleEnd),
2506 EventHint::MarkEnd,
2507 &inner.doc,
2508 )?;
2509
2510 Ok(())
2511 }
2512
2513 pub fn check(&self) {
2514 match &self.inner {
2515 MaybeDetached::Detached(t) => {
2516 let t = t.lock();
2517 t.value.check_consistency_between_content_and_style_ranges();
2518 }
2519 MaybeDetached::Attached(a) => a.with_state(|state| {
2520 state
2521 .as_richtext_state_mut()
2522 .unwrap()
2523 .check_consistency_between_content_and_style_ranges();
2524 }),
2525 }
2526 }
2527
2528 pub fn apply_delta(&self, delta: &[TextDelta]) -> LoroResult<()> {
2529 match &self.inner {
2530 MaybeDetached::Detached(t) => {
2531 let _t = t.lock();
2532 Err(LoroError::NotImplemented(
2534 "`apply_delta` on a detached text container",
2535 ))
2536 }
2537 MaybeDetached::Attached(a) => a.with_txn(|txn| self.apply_delta_with_txn(txn, delta)),
2538 }
2539 }
2540
2541 pub fn apply_delta_with_txn(
2542 &self,
2543 txn: &mut Transaction,
2544 delta: &[TextDelta],
2545 ) -> LoroResult<()> {
2546 let mut index = 0;
2547 struct PendingMark {
2548 start: usize,
2549 end: usize,
2550 attributes: FxHashMap<InternalString, LoroValue>,
2551 }
2552 let mut marks: Vec<PendingMark> = Vec::new();
2553 for d in delta {
2554 match d {
2555 TextDelta::Insert { insert, attributes } => {
2556 let insert_len = event_len(insert.as_str());
2557 if insert_len == 0 {
2558 continue;
2559 }
2560
2561 let mut empty_attr = None;
2562 let attr_ref = attributes.as_ref().unwrap_or_else(|| {
2563 empty_attr = Some(FxHashMap::default());
2564 empty_attr.as_ref().unwrap()
2565 });
2566
2567 let end = checked_delta_index_end(index, insert_len, self.len_event())?;
2568 let override_styles = self.insert_with_txn_and_attr(
2569 txn,
2570 index,
2571 insert.as_str(),
2572 Some(attr_ref),
2573 PosType::Event,
2574 )?;
2575
2576 let mut pending_mark = PendingMark {
2577 start: index,
2578 end,
2579 attributes: FxHashMap::default(),
2580 };
2581 for (key, value) in override_styles {
2582 pending_mark.attributes.insert(key, value);
2583 }
2584 marks.push(pending_mark);
2585 index = end;
2586 }
2587 TextDelta::Delete { delete } => {
2588 self.delete_with_txn(txn, index, *delete, PosType::Event)?;
2589 }
2590 TextDelta::Retain { attributes, retain } => {
2591 let end = checked_delta_index_end(index, *retain, self.len_event())?;
2592 match attributes {
2593 Some(attr) if !attr.is_empty() => {
2594 let mut pending_mark = PendingMark {
2595 start: index,
2596 end,
2597 attributes: FxHashMap::default(),
2598 };
2599 for (key, value) in attr {
2600 pending_mark
2601 .attributes
2602 .insert(key.deref().into(), value.clone());
2603 }
2604 marks.push(pending_mark);
2605 }
2606 _ => {}
2607 }
2608 index = end;
2609 }
2610 }
2611 }
2612
2613 let mut len = match &self.inner {
2614 MaybeDetached::Detached(_) => self.len_event(),
2615 MaybeDetached::Attached(a) => {
2616 a.with_state(|state| state.as_richtext_state_mut().unwrap().len(PosType::Event))
2617 }
2618 };
2619 for pending_mark in marks {
2620 if pending_mark.start >= len {
2621 self.insert_with_txn(
2622 txn,
2623 len,
2624 &"\n".repeat(pending_mark.start - len + 1),
2625 PosType::Event,
2626 )?;
2627 len = pending_mark.start;
2628 }
2629
2630 for (key, value) in pending_mark.attributes {
2631 self.mark_with_txn(
2632 txn,
2633 pending_mark.start,
2634 pending_mark.end,
2635 key.deref(),
2636 value,
2637 PosType::Event,
2638 )?;
2639 }
2640 }
2641
2642 Ok(())
2643 }
2644
2645 pub fn update(&self, text: &str, options: UpdateOptions) -> Result<(), UpdateTimeoutError> {
2646 let old_str = self.to_string();
2647 let new = text.chars().map(|x| x as u32).collect::<Vec<u32>>();
2648 let old = old_str.chars().map(|x| x as u32).collect::<Vec<u32>>();
2649 diff(
2650 &mut OperateProxy::new(text_update::DiffHook::new(self, &new)),
2651 options,
2652 &old,
2653 &new,
2654 )?;
2655 Ok(())
2656 }
2657
2658 pub fn update_by_line(
2659 &self,
2660 text: &str,
2661 options: UpdateOptions,
2662 ) -> Result<(), UpdateTimeoutError> {
2663 let hook = text_update::DiffHookForLine::new(self, text);
2664 let old_lines = hook.get_old_arr().to_vec();
2665 let new_lines = hook.get_new_arr().to_vec();
2666 diff(
2667 &mut OperateProxy::new(hook),
2668 options,
2669 &old_lines,
2670 &new_lines,
2671 )
2672 }
2673
2674 #[allow(clippy::inherent_to_string)]
2675 pub fn to_string(&self) -> String {
2676 match &self.inner {
2677 MaybeDetached::Detached(t) => t.lock().value.to_string(),
2678 MaybeDetached::Attached(a) => a.get_value().into_string().unwrap().unwrap(),
2679 }
2680 }
2681
2682 pub fn get_cursor(&self, event_index: usize, side: Side) -> Option<Cursor> {
2683 self.get_cursor_internal(event_index, side, true)
2684 }
2685
2686 pub(crate) fn get_cursor_internal(
2688 &self,
2689 index: usize,
2690 side: Side,
2691 get_by_event_index: bool,
2692 ) -> Option<Cursor> {
2693 match &self.inner {
2694 MaybeDetached::Detached(_) => None,
2695 MaybeDetached::Attached(a) => {
2696 let (id, len, origin_pos) = a.with_state(|s| {
2697 let s = s.as_richtext_state_mut().unwrap();
2698 (
2699 s.get_stable_position(index, get_by_event_index),
2700 if get_by_event_index {
2701 s.len_event()
2702 } else {
2703 s.len_unicode()
2704 },
2705 if get_by_event_index {
2706 s.event_index_to_unicode_index(index)
2707 } else {
2708 index
2709 },
2710 )
2711 });
2712
2713 if len == 0 {
2714 return Some(Cursor {
2715 id: None,
2716 container: self.id(),
2717 side: if side == Side::Middle {
2718 Side::Left
2719 } else {
2720 side
2721 },
2722 origin_pos: 0,
2723 });
2724 }
2725
2726 if len <= index {
2727 return Some(Cursor {
2728 id: None,
2729 container: self.id(),
2730 side: Side::Right,
2731 origin_pos: len,
2732 });
2733 }
2734
2735 let id = id?;
2736 Some(Cursor {
2737 id: Some(id),
2738 container: self.id(),
2739 side,
2740 origin_pos,
2741 })
2742 }
2743 }
2744 }
2745
2746 pub(crate) fn convert_entity_index_to_event_index(&self, entity_index: usize) -> usize {
2747 match &self.inner {
2748 MaybeDetached::Detached(s) => s.lock().value.entity_index_to_event_index(entity_index),
2749 MaybeDetached::Attached(a) => {
2750 let mut pos = 0;
2751 a.with_state(|s| {
2752 let s = s.as_richtext_state_mut().unwrap();
2753 pos = s.entity_index_to_event_index(entity_index);
2754 });
2755 pos
2756 }
2757 }
2758 }
2759
2760 pub fn get_delta(&self) -> Vec<TextDelta> {
2761 match &self.inner {
2762 MaybeDetached::Detached(s) => {
2763 let mut delta = Vec::new();
2764 for span in s.lock().value.iter() {
2765 if span.text.as_str().is_empty() {
2766 continue;
2767 }
2768
2769 let next_attr = span.attributes.to_option_map();
2770 match delta.last_mut() {
2771 Some(TextDelta::Insert { insert, attributes })
2772 if &next_attr == attributes =>
2773 {
2774 insert.push_str(span.text.as_str());
2775 continue;
2776 }
2777 _ => {}
2778 }
2779
2780 delta.push(TextDelta::Insert {
2781 insert: span.text.as_str().to_string(),
2782 attributes: next_attr,
2783 })
2784 }
2785 delta
2786 }
2787 MaybeDetached::Attached(_a) => self
2788 .with_state(|state| {
2789 let state = state.as_richtext_state_mut().unwrap();
2790 Ok(state.get_delta())
2791 })
2792 .unwrap(),
2793 }
2794 }
2795
2796 pub fn is_deleted(&self) -> bool {
2797 match &self.inner {
2798 MaybeDetached::Detached(_) => false,
2799 MaybeDetached::Attached(a) => a.is_deleted(),
2800 }
2801 }
2802
2803 pub fn push_str(&self, s: &str) -> LoroResult<()> {
2804 self.insert_utf8(self.len_utf8(), s)
2805 }
2806
2807 pub fn clear(&self) -> LoroResult<()> {
2808 match &self.inner {
2809 MaybeDetached::Detached(mutex) => {
2810 let mut t = mutex.lock();
2811 let len = t.value.len_unicode();
2812 let ranges = t.value.get_text_entity_ranges(0, len, PosType::Unicode)?;
2813 for range in ranges.iter().rev() {
2814 t.value
2815 .drain_by_entity_index(range.entity_start, range.entity_len(), None);
2816 }
2817 Ok(())
2818 }
2819 MaybeDetached::Attached(a) => a.with_txn(|txn| {
2820 let len = a.with_state(|s| s.as_richtext_state_mut().unwrap().len_unicode());
2821 self.delete_with_txn_inline(txn, 0, len, PosType::Unicode)
2822 }),
2823 }
2824 }
2825
2826 pub fn convert_pos(&self, index: usize, from: PosType, to: PosType) -> Option<usize> {
2831 if from == to {
2832 return Some(index);
2833 }
2834
2835 if matches!(from, PosType::Entity) || matches!(to, PosType::Entity) {
2836 return None;
2837 }
2838
2839 let (event_index, unicode_index) = match &self.inner {
2841 MaybeDetached::Detached(t) => {
2842 let t = t.lock();
2843 if index > t.value.len(from) {
2844 return None;
2845 }
2846 let event_index = if from == PosType::Event {
2847 index
2848 } else {
2849 t.value.index_to_event_index(index, from)
2850 };
2851 let unicode_index = if from == PosType::Unicode {
2852 index
2853 } else {
2854 t.value.event_index_to_unicode_index(event_index)
2855 };
2856 (event_index, unicode_index)
2857 }
2858 MaybeDetached::Attached(a) if a.has_decoded_state() => {
2859 let res: Option<(usize, usize)> = a.with_state(|state| {
2860 let state = state.as_richtext_state_mut().unwrap();
2861 if index > state.len(from) {
2862 return None;
2863 }
2864
2865 let event_index = if from == PosType::Event {
2866 index
2867 } else {
2868 state.index_to_event_index(index, from)
2869 };
2870 let unicode_index = if from == PosType::Unicode {
2871 index
2872 } else {
2873 state.event_index_to_unicode_index(event_index)
2874 };
2875 Some((event_index, unicode_index))
2876 });
2877
2878 res?
2879 }
2880 MaybeDetached::Attached(a) => {
2881 let value = a.get_value();
2882 let s = value.as_string().unwrap();
2883 let unicode_index = text_pos_to_unicode(s, index, from)?;
2884 let event_index = unicode_to_text_pos(s, unicode_index, PosType::Event)?;
2885 (event_index, unicode_index)
2886 }
2887 };
2888
2889 let result = match to {
2890 PosType::Unicode => Some(unicode_index),
2891 PosType::Event => Some(event_index),
2892 PosType::Bytes | PosType::Utf16 => {
2893 match &self.inner {
2897 MaybeDetached::Detached(t) => {
2898 let t = t.lock();
2899 if event_index > t.value.len_event() {
2900 return None;
2901 }
2902 Some(t.value.event_index_to_index(event_index, to))
2903 }
2904 MaybeDetached::Attached(a) if a.has_decoded_state() => a.with_state(|state| {
2905 let state = state.as_richtext_state_mut().unwrap();
2906 if event_index > state.len_event() {
2907 return None;
2908 }
2909 Some(state.event_index_to_index(event_index, to))
2910 }),
2911 MaybeDetached::Attached(a) => {
2912 let value = a.get_value();
2913 let s = value.as_string().unwrap();
2914 unicode_to_text_pos(s, unicode_index, to)
2915 }
2916 }
2917 }
2918 PosType::Entity => None,
2919 };
2920 result
2921 }
2922}
2923
2924fn event_len(s: &str) -> usize {
2925 if cfg!(feature = "wasm") {
2926 count_utf16_len(s.as_bytes())
2927 } else {
2928 s.chars().count()
2929 }
2930}
2931
2932fn text_len(s: &str, pos_type: PosType) -> Option<usize> {
2933 Some(match pos_type {
2934 PosType::Bytes => s.len(),
2935 PosType::Unicode => s.chars().count(),
2936 PosType::Utf16 => count_utf16_len(s.as_bytes()),
2937 PosType::Event => event_len(s),
2938 PosType::Entity => return None,
2939 })
2940}
2941
2942fn text_pos_to_unicode(s: &str, index: usize, pos_type: PosType) -> Option<usize> {
2943 match pos_type {
2944 PosType::Unicode => (index <= s.chars().count()).then_some(index),
2945 PosType::Bytes => {
2946 if index > s.len() {
2947 None
2948 } else {
2949 Some(
2950 s.char_indices()
2951 .take_while(|(pos, c)| *pos + c.len_utf8() <= index)
2952 .count(),
2953 )
2954 }
2955 }
2956 PosType::Utf16 => utf16_to_unicode_pos(s, index),
2957 PosType::Event if cfg!(feature = "wasm") => utf16_to_unicode_pos(s, index),
2958 PosType::Event => (index <= s.chars().count()).then_some(index),
2959 PosType::Entity => None,
2960 }
2961}
2962
2963fn unicode_to_text_pos(s: &str, index: usize, pos_type: PosType) -> Option<usize> {
2964 match pos_type {
2965 PosType::Unicode => (index <= s.chars().count()).then_some(index),
2966 PosType::Bytes => unicode_to_byte_pos(s, index),
2967 PosType::Utf16 => unicode_to_utf16_pos(s, index),
2968 PosType::Event if cfg!(feature = "wasm") => unicode_to_utf16_pos(s, index),
2969 PosType::Event => (index <= s.chars().count()).then_some(index),
2970 PosType::Entity => None,
2971 }
2972}
2973
2974fn unicode_to_byte_pos(s: &str, index: usize) -> Option<usize> {
2975 if index == 0 {
2976 return Some(0);
2977 }
2978
2979 let mut unicode_pos = 0;
2980 for (byte_pos, _) in s.char_indices() {
2981 if unicode_pos == index {
2982 return Some(byte_pos);
2983 }
2984 unicode_pos += 1;
2985 }
2986
2987 (unicode_pos == index).then_some(s.len())
2988}
2989
2990fn unicode_to_utf16_pos(s: &str, index: usize) -> Option<usize> {
2991 let mut unicode_pos = 0;
2992 let mut utf16_pos = 0;
2993 if index == 0 {
2994 return Some(0);
2995 }
2996
2997 for c in s.chars() {
2998 unicode_pos += 1;
2999 utf16_pos += c.len_utf16();
3000 if unicode_pos == index {
3001 return Some(utf16_pos);
3002 }
3003 }
3004
3005 (unicode_pos == index).then_some(utf16_pos)
3006}
3007
3008fn utf16_to_unicode_pos(s: &str, index: usize) -> Option<usize> {
3009 let mut unicode_pos = 0;
3010 let mut utf16_pos = 0;
3011 if index == 0 {
3012 return Some(0);
3013 }
3014
3015 for c in s.chars() {
3016 let next_utf16_pos = utf16_pos + c.len_utf16();
3017 if index < next_utf16_pos {
3018 return Some(unicode_pos);
3019 }
3020 if index == next_utf16_pos {
3021 return Some(unicode_pos + 1);
3022 }
3023 utf16_pos = next_utf16_pos;
3024 unicode_pos += 1;
3025 }
3026
3027 (index == utf16_pos).then_some(unicode_pos)
3028}
3029
3030fn text_boundary_error(pos: usize, pos_type: PosType) -> LoroError {
3031 match pos_type {
3032 PosType::Bytes => LoroError::UTF8InUnicodeCodePoint { pos },
3033 PosType::Utf16 => LoroError::UTF16InUnicodeCodePoint { pos },
3034 PosType::Event if cfg!(feature = "wasm") => LoroError::UTF16InUnicodeCodePoint { pos },
3035 _ => LoroError::OutOfBound {
3036 pos,
3037 len: 0,
3038 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3039 },
3040 }
3041}
3042
3043fn text_char_at(s: &str, pos: usize, pos_type: PosType) -> LoroResult<char> {
3044 let len = text_len(s, pos_type).unwrap_or(0);
3045 if pos >= len {
3046 return Err(LoroError::OutOfBound {
3047 pos,
3048 len,
3049 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3050 });
3051 }
3052
3053 let unicode_pos =
3054 text_pos_to_unicode(s, pos, pos_type).ok_or_else(|| text_boundary_error(pos, pos_type))?;
3055 s.chars().nth(unicode_pos).ok_or(LoroError::OutOfBound {
3056 pos,
3057 len,
3058 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3059 })
3060}
3061
3062fn text_slice(s: &str, start: usize, end: usize, pos_type: PosType) -> LoroResult<String> {
3063 if end < start {
3064 return Err(LoroError::EndIndexLessThanStartIndex { start, end });
3065 }
3066 if start == end {
3067 return Ok(String::new());
3068 }
3069
3070 let len = text_len(s, pos_type).unwrap_or(0);
3071 if end > len {
3072 return Err(LoroError::OutOfBound {
3073 pos: end,
3074 len,
3075 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3076 });
3077 }
3078
3079 let start = text_pos_to_unicode(s, start, pos_type)
3080 .ok_or_else(|| text_boundary_error(start, pos_type))?;
3081 let end =
3082 text_pos_to_unicode(s, end, pos_type).ok_or_else(|| text_boundary_error(end, pos_type))?;
3083 let start = unicode_to_byte_pos(s, start).expect("unicode index must map to a byte boundary");
3084 let end = unicode_to_byte_pos(s, end).expect("unicode index must map to a byte boundary");
3085 Ok(s[start..end].to_string())
3086}
3087
3088impl ListHandler {
3089 pub fn new_detached() -> Self {
3093 Self {
3094 inner: MaybeDetached::new_detached(Vec::new()),
3095 }
3096 }
3097
3098 pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3099 match &self.inner {
3100 MaybeDetached::Detached(l) => {
3101 let mut list = l.lock();
3102 let len = list.value.len();
3103 if pos > len {
3104 return Err(LoroError::OutOfBound {
3105 pos,
3106 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3107 len,
3108 });
3109 }
3110 let value = v.into();
3111 ensure_no_regular_container_value(&value)?;
3112 list.value.insert(pos, ValueOrHandler::Value(value));
3113 Ok(())
3114 }
3115 MaybeDetached::Attached(a) => {
3116 a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
3117 }
3118 }
3119 }
3120
3121 pub fn insert_with_txn(
3122 &self,
3123 txn: &mut Transaction,
3124 pos: usize,
3125 v: LoroValue,
3126 ) -> LoroResult<()> {
3127 if pos > self.len() {
3128 return Err(LoroError::OutOfBound {
3129 pos,
3130 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3131 len: self.len(),
3132 });
3133 }
3134
3135 let inner = self.inner.try_attached_state()?;
3136 ensure_no_regular_container_value(&v)?;
3137
3138 txn.apply_local_op(
3139 inner.container_idx,
3140 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3141 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3142 pos,
3143 }),
3144 EventHint::InsertList { len: 1, pos },
3145 &inner.doc,
3146 )
3147 }
3148
3149 pub fn push(&self, v: impl Into<LoroValue>) -> LoroResult<()> {
3150 match &self.inner {
3151 MaybeDetached::Detached(l) => {
3152 let mut list = l.lock();
3153 let value = v.into();
3154 ensure_no_regular_container_value(&value)?;
3155 list.value.push(ValueOrHandler::Value(value));
3156 Ok(())
3157 }
3158 MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v.into())),
3159 }
3160 }
3161
3162 pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
3163 let pos = self.len();
3164 self.insert_with_txn(txn, pos, v)
3165 }
3166
3167 pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
3168 match &self.inner {
3169 MaybeDetached::Detached(l) => {
3170 let mut list = l.lock();
3171 Ok(list.value.pop().map(|v| v.to_value()))
3172 }
3173 MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
3174 }
3175 }
3176
3177 pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
3178 let len = self.len();
3179 if len == 0 {
3180 return Ok(None);
3181 }
3182
3183 let v = self.get(len - 1);
3184 self.delete_with_txn(txn, len - 1, 1)?;
3185 Ok(v)
3186 }
3187
3188 pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3189 match &self.inner {
3190 MaybeDetached::Detached(l) => {
3191 let mut list = l.lock();
3192 if pos > list.value.len() {
3193 return Err(LoroError::OutOfBound {
3194 pos,
3195 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3196 len: list.value.len(),
3197 });
3198 }
3199 list.value
3200 .insert(pos, ValueOrHandler::Handler(child.to_handler()));
3201 Ok(child)
3202 }
3203 MaybeDetached::Attached(a) => {
3204 a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
3205 }
3206 }
3207 }
3208
3209 pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
3210 self.insert_container(self.len(), child)
3211 }
3212
3213 pub fn insert_container_with_txn<H: HandlerTrait>(
3214 &self,
3215 txn: &mut Transaction,
3216 pos: usize,
3217 child: H,
3218 ) -> LoroResult<H> {
3219 if pos > self.len() {
3220 return Err(LoroError::OutOfBound {
3221 pos,
3222 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3223 len: self.len(),
3224 });
3225 }
3226
3227 let inner = self.inner.try_attached_state()?;
3228 let id = txn.next_id();
3229 let container_id = ContainerID::new_normal(id, child.kind());
3230 let v = LoroValue::Container(container_id.clone());
3231 txn.apply_local_op(
3232 inner.container_idx,
3233 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3234 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3235 pos,
3236 }),
3237 EventHint::InsertList { len: 1, pos },
3238 &inner.doc,
3239 )?;
3240 let ans = child.attach(txn, inner, container_id)?;
3241 Ok(ans)
3242 }
3243
3244 pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3245 match &self.inner {
3246 MaybeDetached::Detached(l) => {
3247 let mut list = l.lock();
3248 let end = checked_range_end(
3249 pos,
3250 len,
3251 list.value.len(),
3252 || format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3253 )?;
3254 list.value.drain(pos..end);
3255 Ok(())
3256 }
3257 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
3258 }
3259 }
3260
3261 pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
3262 if len == 0 {
3263 return Ok(());
3264 }
3265
3266 let list_len = self.len();
3267 let end = checked_range_end(
3268 pos,
3269 len,
3270 list_len,
3271 || format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3272 )?;
3273
3274 let inner = self.inner.try_attached_state()?;
3275 let ids: Vec<_> = inner.with_state(|state| {
3276 let list = state.as_list_state().unwrap();
3277 (pos..end).map(|i| list.get_id_at(i).unwrap()).collect()
3278 });
3279
3280 for id in ids.into_iter() {
3281 txn.apply_local_op(
3282 inner.container_idx,
3283 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
3284 id.id(),
3285 pos as isize,
3286 1,
3287 ))),
3288 EventHint::DeleteList(DeleteSpan::new(pos as isize, 1)),
3289 &inner.doc,
3290 )?;
3291 }
3292
3293 Ok(())
3294 }
3295
3296 pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
3297 match &self.inner {
3298 MaybeDetached::Detached(l) => {
3299 let list = l.lock();
3300 let value = list.value.get(index).ok_or(LoroError::OutOfBound {
3301 pos: index,
3302 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3303 len: list.value.len(),
3304 })?;
3305 match value {
3306 ValueOrHandler::Handler(h) => Ok(h.clone()),
3307 _ => Err(LoroError::ArgErr(
3308 format!(
3309 "Expected container at index {}, but found {:?}",
3310 index, value
3311 )
3312 .into_boxed_str(),
3313 )),
3314 }
3315 }
3316 MaybeDetached::Attached(_) => {
3317 let Some(value) = self.get_(index) else {
3318 return Err(LoroError::OutOfBound {
3319 pos: index,
3320 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3321 len: self.len(),
3322 });
3323 };
3324 match value {
3325 ValueOrHandler::Handler(handler) => Ok(handler),
3326 ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
3327 format!(
3328 "Expected container at index {}, but found {:?}",
3329 index, value
3330 )
3331 .into_boxed_str(),
3332 )),
3333 }
3334 }
3335 }
3336 }
3337
3338 pub fn len(&self) -> usize {
3339 match &self.inner {
3340 MaybeDetached::Detached(l) => l.lock().value.len(),
3341 MaybeDetached::Attached(a) => {
3342 a.with_doc_state(|state| state.get_list_len(a.container_idx))
3343 }
3344 }
3345 }
3346
3347 pub fn is_empty(&self) -> bool {
3348 self.len() == 0
3349 }
3350
3351 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
3352 let inner = self.inner.try_attached_state()?;
3353 Ok(inner.with_doc_state(|state| {
3354 state.get_container_deep_value_with_id(inner.container_idx, None)
3355 }))
3356 }
3357
3358 pub fn get(&self, index: usize) -> Option<LoroValue> {
3359 match &self.inner {
3360 MaybeDetached::Detached(l) => l.lock().value.get(index).map(|x| x.to_value()),
3361 MaybeDetached::Attached(a) => {
3362 a.with_doc_state(|state| state.get_list_value_at(a.container_idx, index))
3363 }
3364 }
3365 }
3366
3367 pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
3369 match &self.inner {
3370 MaybeDetached::Detached(l) => {
3371 let l = l.lock();
3372 l.value.get(index).cloned()
3373 }
3374 MaybeDetached::Attached(inner) => {
3375 let value = inner
3376 .with_doc_state(|state| state.get_list_value_at(inner.container_idx, index));
3377 value.map(|value| value_to_value_or_handler(inner, value))
3378 }
3379 }
3380 }
3381
3382 pub fn for_each<I>(&self, mut f: I)
3383 where
3384 I: FnMut(ValueOrHandler),
3385 {
3386 match &self.inner {
3387 MaybeDetached::Detached(l) => {
3388 let l = l.lock();
3389 for v in l.value.iter() {
3390 f(v.clone())
3391 }
3392 }
3393 MaybeDetached::Attached(inner) => {
3394 let temp = inner.with_doc_state(|state| {
3395 state
3396 .get_list_values(inner.container_idx)
3397 .into_iter()
3398 .map(|value| value_to_value_or_handler(inner, value))
3399 .collect::<Vec<_>>()
3400 });
3401 for v in temp.into_iter() {
3402 f(v);
3403 }
3404 }
3405 }
3406 }
3407
3408 pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
3409 match &self.inner {
3410 MaybeDetached::Detached(_) => None,
3411 MaybeDetached::Attached(a) => {
3412 let (id, len) = a.with_state(|s| {
3413 let l = s.as_list_state().unwrap();
3414 (l.get_id_at(pos), l.len())
3415 });
3416
3417 if len == 0 {
3418 return Some(Cursor {
3419 id: None,
3420 container: self.id(),
3421 side: if side == Side::Middle {
3422 Side::Left
3423 } else {
3424 side
3425 },
3426 origin_pos: 0,
3427 });
3428 }
3429
3430 if len <= pos {
3431 return Some(Cursor {
3432 id: None,
3433 container: self.id(),
3434 side: Side::Right,
3435 origin_pos: len,
3436 });
3437 }
3438
3439 let id = id?;
3440 Some(Cursor {
3441 id: Some(id.id()),
3442 container: self.id(),
3443 side,
3444 origin_pos: pos,
3445 })
3446 }
3447 }
3448 }
3449
3450 fn apply_delta(
3451 &self,
3452 delta: loro_delta::DeltaRope<
3453 loro_delta::array_vec::ArrayVec<ValueOrHandler, 8>,
3454 crate::event::ListDeltaMeta,
3455 >,
3456 on_container_remap: &mut dyn FnMut(ContainerID, ContainerID),
3457 ) -> LoroResult<()> {
3458 match &self.inner {
3459 MaybeDetached::Detached(_) => unimplemented!(),
3460 MaybeDetached::Attached(_) => {
3461 let mut index = 0;
3462 for item in delta.iter() {
3463 match item {
3464 loro_delta::DeltaItem::Retain { len, .. } => {
3465 index += len;
3466 }
3467 loro_delta::DeltaItem::Replace { value, delete, .. } => {
3468 if *delete > 0 {
3469 self.delete(index, *delete)?;
3470 }
3471
3472 for v in value.iter() {
3473 match v {
3474 ValueOrHandler::Value(LoroValue::Container(old_id)) => {
3475 let new_h = self.insert_container(
3476 index,
3477 Handler::new_unattached(old_id.container_type()),
3478 )?;
3479 let new_id = new_h.id();
3480 on_container_remap(old_id.clone(), new_id);
3481 }
3482 ValueOrHandler::Handler(h) => {
3483 let old_id = h.id();
3484 let new_h = self.insert_container(
3485 index,
3486 Handler::new_unattached(old_id.container_type()),
3487 )?;
3488 let new_id = new_h.id();
3489 on_container_remap(old_id, new_id);
3490 }
3491 ValueOrHandler::Value(v) => {
3492 self.insert(index, v.clone())?;
3493 }
3494 }
3495
3496 index += 1;
3497 }
3498 }
3499 }
3500 }
3501 }
3502 }
3503
3504 Ok(())
3505 }
3506
3507 pub fn is_deleted(&self) -> bool {
3508 match &self.inner {
3509 MaybeDetached::Detached(_) => false,
3510 MaybeDetached::Attached(a) => a.is_deleted(),
3511 }
3512 }
3513
3514 pub fn clear(&self) -> LoroResult<()> {
3515 match &self.inner {
3516 MaybeDetached::Detached(l) => {
3517 let mut l = l.lock();
3518 l.value.clear();
3519 Ok(())
3520 }
3521 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
3522 }
3523 }
3524
3525 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
3526 self.delete_with_txn(txn, 0, self.len())
3527 }
3528
3529 pub fn get_id_at(&self, pos: usize) -> Option<ID> {
3530 match &self.inner {
3531 MaybeDetached::Detached(_) => None,
3532 MaybeDetached::Attached(a) => a.with_state(|state| {
3533 state
3534 .as_list_state()
3535 .unwrap()
3536 .get_id_at(pos)
3537 .map(|x| x.id())
3538 }),
3539 }
3540 }
3541}
3542
3543impl MovableListHandler {
3544 pub fn insert(&self, pos: usize, v: impl Into<LoroValue>) -> LoroResult<()> {
3545 match &self.inner {
3546 MaybeDetached::Detached(d) => {
3547 let mut d = d.lock();
3548 if pos > d.value.len() {
3549 return Err(LoroError::OutOfBound {
3550 pos,
3551 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3552 len: d.value.len(),
3553 });
3554 }
3555 let value = v.into();
3556 ensure_no_regular_container_value(&value)?;
3557 d.value.insert(pos, ValueOrHandler::Value(value));
3558 Ok(())
3559 }
3560 MaybeDetached::Attached(a) => {
3561 a.with_txn(|txn| self.insert_with_txn(txn, pos, v.into()))
3562 }
3563 }
3564 }
3565
3566 #[instrument(skip_all)]
3567 pub fn insert_with_txn(
3568 &self,
3569 txn: &mut Transaction,
3570 pos: usize,
3571 v: LoroValue,
3572 ) -> LoroResult<()> {
3573 if pos > self.len() {
3574 return Err(LoroError::OutOfBound {
3575 pos,
3576 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3577 len: self.len(),
3578 });
3579 }
3580
3581 ensure_no_regular_container_value(&v)?;
3582
3583 let op_index = self.with_state(|state| {
3584 let list = state.as_movable_list_state().unwrap();
3585 Ok(list
3586 .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3587 .unwrap())
3588 })?;
3589
3590 let inner = self.inner.try_attached_state()?;
3591 txn.apply_local_op(
3592 inner.container_idx,
3593 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3594 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3595 pos: op_index,
3596 }),
3597 EventHint::InsertList { len: 1, pos },
3598 &inner.doc,
3599 )
3600 }
3601
3602 #[inline]
3603 pub fn mov(&self, from: usize, to: usize) -> LoroResult<()> {
3604 match &self.inner {
3605 MaybeDetached::Detached(d) => {
3606 let mut d = d.lock();
3607 if from >= d.value.len() {
3608 return Err(LoroError::OutOfBound {
3609 pos: from,
3610 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3611 len: d.value.len(),
3612 });
3613 }
3614 if to >= d.value.len() {
3615 return Err(LoroError::OutOfBound {
3616 pos: to,
3617 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3618 len: d.value.len(),
3619 });
3620 }
3621 let v = d.value.remove(from);
3622 d.value.insert(to, v);
3623 Ok(())
3624 }
3625 MaybeDetached::Attached(a) => a.with_txn(|txn| self.move_with_txn(txn, from, to)),
3626 }
3627 }
3628
3629 #[instrument(skip_all)]
3631 pub fn move_with_txn(&self, txn: &mut Transaction, from: usize, to: usize) -> LoroResult<()> {
3632 if from == to {
3633 return Ok(());
3634 }
3635
3636 if from >= self.len() {
3637 return Err(LoroError::OutOfBound {
3638 pos: from,
3639 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3640 len: self.len(),
3641 });
3642 }
3643
3644 if to >= self.len() {
3645 return Err(LoroError::OutOfBound {
3646 pos: to,
3647 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3648 len: self.len(),
3649 });
3650 }
3651
3652 let (op_from, op_to, elem_id, value) = self.with_state(|state| {
3653 let list = state.as_movable_list_state().unwrap();
3654 let (elem_id, elem) = list
3655 .get_elem_at_given_pos(from, IndexType::ForUser)
3656 .unwrap();
3657 Ok((
3658 list.convert_index(from, IndexType::ForUser, IndexType::ForOp)
3659 .unwrap(),
3660 list.convert_index(to, IndexType::ForUser, IndexType::ForOp)
3661 .unwrap(),
3662 elem_id,
3663 elem.value().clone(),
3664 ))
3665 })?;
3666
3667 let inner = self.inner.try_attached_state()?;
3668 txn.apply_local_op(
3669 inner.container_idx,
3670 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Move {
3671 from: op_from as u32,
3672 to: op_to as u32,
3673 elem_id: elem_id.to_id(),
3674 }),
3675 EventHint::Move {
3676 value,
3677 from: from as u32,
3678 to: to as u32,
3679 },
3680 &inner.doc,
3681 )
3682 }
3683
3684 pub fn push(&self, v: LoroValue) -> LoroResult<()> {
3685 match &self.inner {
3686 MaybeDetached::Detached(d) => {
3687 let mut d = d.lock();
3688 d.value.push(v.into());
3689 Ok(())
3690 }
3691 MaybeDetached::Attached(a) => a.with_txn(|txn| self.push_with_txn(txn, v)),
3692 }
3693 }
3694
3695 pub fn push_with_txn(&self, txn: &mut Transaction, v: LoroValue) -> LoroResult<()> {
3696 let pos = self.len();
3697 self.insert_with_txn(txn, pos, v)
3698 }
3699
3700 pub fn pop_(&self) -> LoroResult<Option<ValueOrHandler>> {
3701 match &self.inner {
3702 MaybeDetached::Detached(d) => {
3703 let mut d = d.lock();
3704 Ok(d.value.pop())
3705 }
3706 MaybeDetached::Attached(a) => {
3707 if self.is_empty() {
3708 return Ok(None);
3709 }
3710 let last = self.len() - 1;
3711 let ans = self.get_(last);
3712 a.with_txn(|txn| self.pop_with_txn(txn))?;
3713 Ok(ans)
3714 }
3715 }
3716 }
3717
3718 pub fn pop(&self) -> LoroResult<Option<LoroValue>> {
3719 match &self.inner {
3720 MaybeDetached::Detached(a) => {
3721 let mut a = a.lock();
3722 Ok(a.value.pop().map(|x| x.to_value()))
3723 }
3724 MaybeDetached::Attached(a) => a.with_txn(|txn| self.pop_with_txn(txn)),
3725 }
3726 }
3727
3728 pub fn pop_with_txn(&self, txn: &mut Transaction) -> LoroResult<Option<LoroValue>> {
3729 let len = self.len();
3730 if len == 0 {
3731 return Ok(None);
3732 }
3733
3734 let v = self.get(len - 1);
3735 self.delete_with_txn(txn, len - 1, 1)?;
3736 Ok(v)
3737 }
3738
3739 pub fn insert_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3740 match &self.inner {
3741 MaybeDetached::Detached(d) => {
3742 let mut d = d.lock();
3743 if pos > d.value.len() {
3744 return Err(LoroError::OutOfBound {
3745 pos,
3746 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3747 len: d.value.len(),
3748 });
3749 }
3750 d.value
3751 .insert(pos, ValueOrHandler::Handler(child.to_handler()));
3752 Ok(child)
3753 }
3754 MaybeDetached::Attached(a) => {
3755 a.with_txn(|txn| self.insert_container_with_txn(txn, pos, child))
3756 }
3757 }
3758 }
3759
3760 pub fn push_container<H: HandlerTrait>(&self, child: H) -> LoroResult<H> {
3761 self.insert_container(self.len(), child)
3762 }
3763
3764 pub fn insert_container_with_txn<H: HandlerTrait>(
3765 &self,
3766 txn: &mut Transaction,
3767 pos: usize,
3768 child: H,
3769 ) -> LoroResult<H> {
3770 if pos > self.len() {
3771 return Err(LoroError::OutOfBound {
3772 pos,
3773 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3774 len: self.len(),
3775 });
3776 }
3777
3778 let op_index = self.with_state(|state| {
3779 let list = state.as_movable_list_state().unwrap();
3780 Ok(list
3781 .convert_index(pos, IndexType::ForUser, IndexType::ForOp)
3782 .unwrap())
3783 })?;
3784
3785 let id = txn.next_id();
3786 let container_id = ContainerID::new_normal(id, child.kind());
3787 let v = LoroValue::Container(container_id.clone());
3788 let inner = self.inner.try_attached_state()?;
3789 txn.apply_local_op(
3790 inner.container_idx,
3791 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
3792 slice: ListSlice::RawData(Cow::Owned(vec![v.clone()])),
3793 pos: op_index,
3794 }),
3795 EventHint::InsertList { len: 1, pos },
3796 &inner.doc,
3797 )?;
3798 child.attach(txn, inner, container_id)
3799 }
3800
3801 pub fn set(&self, index: usize, value: impl Into<LoroValue>) -> LoroResult<()> {
3802 match &self.inner {
3803 MaybeDetached::Detached(d) => {
3804 let mut d = d.lock();
3805 if index >= d.value.len() {
3806 return Err(LoroError::OutOfBound {
3807 pos: index,
3808 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3809 len: d.value.len(),
3810 });
3811 }
3812 let value = value.into();
3813 ensure_no_regular_container_value(&value)?;
3814 d.value[index] = ValueOrHandler::Value(value);
3815 Ok(())
3816 }
3817 MaybeDetached::Attached(a) => {
3818 a.with_txn(|txn| self.set_with_txn(txn, index, value.into()))
3819 }
3820 }
3821 }
3822
3823 pub fn set_with_txn(
3824 &self,
3825 txn: &mut Transaction,
3826 index: usize,
3827 value: LoroValue,
3828 ) -> LoroResult<()> {
3829 if index >= self.len() {
3830 return Err(LoroError::OutOfBound {
3831 pos: index,
3832 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3833 len: self.len(),
3834 });
3835 }
3836
3837 let inner = self.inner.try_attached_state()?;
3838 let Some(elem_id) = self.with_state(|state| {
3839 let list = state.as_movable_list_state().unwrap();
3840 Ok(list.get_elem_id_at(index, IndexType::ForUser))
3841 })?
3842 else {
3843 unreachable!()
3844 };
3845 ensure_no_regular_container_value(&value)?;
3846
3847 let op = crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3848 elem_id: elem_id.to_id(),
3849 value: value.clone(),
3850 });
3851
3852 let hint = EventHint::SetList { index, value };
3853 txn.apply_local_op(inner.container_idx, op, hint, &inner.doc)
3854 }
3855
3856 pub fn set_container<H: HandlerTrait>(&self, pos: usize, child: H) -> LoroResult<H> {
3857 match &self.inner {
3858 MaybeDetached::Detached(d) => {
3859 let mut d = d.lock();
3860 if pos >= d.value.len() {
3861 return Err(LoroError::OutOfBound {
3862 pos,
3863 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3864 len: d.value.len(),
3865 });
3866 }
3867 d.value[pos] = ValueOrHandler::Handler(child.to_handler());
3868 Ok(child)
3869 }
3870 MaybeDetached::Attached(a) => {
3871 a.with_txn(|txn| self.set_container_with_txn(txn, pos, child))
3872 }
3873 }
3874 }
3875
3876 pub fn set_container_with_txn<H: HandlerTrait>(
3877 &self,
3878 txn: &mut Transaction,
3879 pos: usize,
3880 child: H,
3881 ) -> LoroResult<H> {
3882 let id = txn.next_id();
3883 let container_id = ContainerID::new_normal(id, child.kind());
3884 let v = LoroValue::Container(container_id.clone());
3885 let Some(elem_id) = self.with_state(|state| {
3886 let list = state.as_movable_list_state().unwrap();
3887 Ok(list.get_elem_id_at(pos, IndexType::ForUser))
3888 })?
3889 else {
3890 let len = self.len();
3891 if pos >= len {
3892 return Err(LoroError::OutOfBound {
3893 pos,
3894 len,
3895 info: "".into(),
3896 });
3897 } else {
3898 unreachable!()
3899 }
3900 };
3901 let inner = self.inner.try_attached_state()?;
3902 txn.apply_local_op(
3903 inner.container_idx,
3904 crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Set {
3905 elem_id: elem_id.to_id(),
3906 value: v.clone(),
3907 }),
3908 EventHint::SetList {
3909 index: pos,
3910 value: v,
3911 },
3912 &inner.doc,
3913 )?;
3914
3915 child.attach(txn, inner, container_id)
3916 }
3917
3918 pub fn delete(&self, pos: usize, len: usize) -> LoroResult<()> {
3919 match &self.inner {
3920 MaybeDetached::Detached(d) => {
3921 let mut d = d.lock();
3922 let end = checked_range_end(
3923 pos,
3924 len,
3925 d.value.len(),
3926 || format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3927 )?;
3928 d.value.drain(pos..end);
3929 Ok(())
3930 }
3931 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, pos, len)),
3932 }
3933 }
3934
3935 #[instrument(skip_all)]
3936 pub fn delete_with_txn(&self, txn: &mut Transaction, pos: usize, len: usize) -> LoroResult<()> {
3937 if len == 0 {
3938 return Ok(());
3939 }
3940
3941 let list_len = self.len();
3942 let end = checked_range_end(
3943 pos,
3944 len,
3945 list_len,
3946 || format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3947 )?;
3948
3949 let (ids, new_poses) = self.with_state(|state| {
3950 let list = state.as_movable_list_state().unwrap();
3951 let ids: Vec<_> = (pos..end)
3952 .map(|i| list.get_list_id_at(i, IndexType::ForUser).unwrap())
3953 .collect();
3954 let poses: Vec<_> = (pos..end)
3955 .map(|user_index| {
3957 let op_index = list
3958 .convert_index(user_index, IndexType::ForUser, IndexType::ForOp)
3959 .unwrap();
3960 assert!(op_index >= user_index);
3961 op_index - (user_index - pos)
3962 })
3963 .collect();
3964 Ok((ids, poses))
3965 })?;
3966
3967 loro_common::info!(?pos, ?len, ?ids, ?new_poses, "delete_with_txn");
3968 let user_pos = pos;
3969 let inner = self.inner.try_attached_state()?;
3970 for (id, op_pos) in ids.into_iter().zip(new_poses.into_iter()) {
3971 txn.apply_local_op(
3972 inner.container_idx,
3973 crate::op::RawOpContent::List(ListOp::Delete(DeleteSpanWithId::new(
3974 id,
3975 op_pos as isize,
3976 1,
3977 ))),
3978 EventHint::DeleteList(DeleteSpan::new(user_pos as isize, 1)),
3979 &inner.doc,
3980 )?;
3981 }
3982
3983 Ok(())
3984 }
3985
3986 pub fn get_child_handler(&self, index: usize) -> LoroResult<Handler> {
3987 match &self.inner {
3988 MaybeDetached::Detached(l) => {
3989 let list = l.lock();
3990 let value = list.value.get(index).ok_or(LoroError::OutOfBound {
3991 pos: index,
3992 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
3993 len: list.value.len(),
3994 })?;
3995 match value {
3996 ValueOrHandler::Handler(h) => Ok(h.clone()),
3997 _ => Err(LoroError::ArgErr(
3998 format!(
3999 "Expected container at index {}, but found {:?}",
4000 index, value
4001 )
4002 .into_boxed_str(),
4003 )),
4004 }
4005 }
4006 MaybeDetached::Attached(_) => {
4007 let Some(value) = self.get_(index) else {
4008 return Err(LoroError::OutOfBound {
4009 pos: index,
4010 info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(),
4011 len: self.len(),
4012 });
4013 };
4014 match value {
4015 ValueOrHandler::Handler(handler) => Ok(handler),
4016 ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
4017 format!(
4018 "Expected container at index {}, but found {:?}",
4019 index, value
4020 )
4021 .into_boxed_str(),
4022 )),
4023 }
4024 }
4025 }
4026 }
4027
4028 pub fn len(&self) -> usize {
4029 match &self.inner {
4030 MaybeDetached::Detached(d) => {
4031 let d = d.lock();
4032 d.value.len()
4033 }
4034 MaybeDetached::Attached(a) => {
4035 a.with_doc_state(|state| state.get_list_len(a.container_idx))
4036 }
4037 }
4038 }
4039
4040 pub fn is_empty(&self) -> bool {
4041 self.len() == 0
4042 }
4043
4044 pub fn get_deep_value_with_id(&self) -> LoroValue {
4045 let inner = self.inner.try_attached_state().unwrap();
4046 inner
4047 .doc
4048 .state
4049 .lock()
4050 .get_container_deep_value_with_id(inner.container_idx, None)
4051 }
4052
4053 pub fn get(&self, index: usize) -> Option<LoroValue> {
4054 match &self.inner {
4055 MaybeDetached::Detached(d) => {
4056 let d = d.lock();
4057 d.value.get(index).map(|v| v.to_value())
4058 }
4059 MaybeDetached::Attached(a) => {
4060 a.with_doc_state(|state| state.get_list_value_at(a.container_idx, index))
4061 }
4062 }
4063 }
4064
4065 pub fn get_(&self, index: usize) -> Option<ValueOrHandler> {
4067 match &self.inner {
4068 MaybeDetached::Detached(d) => {
4069 let d = d.lock();
4070 d.value.get(index).cloned()
4071 }
4072 MaybeDetached::Attached(m) => {
4073 let value =
4074 m.with_doc_state(|state| state.get_list_value_at(m.container_idx, index));
4075 value.map(|value| value_to_value_or_handler(m, value))
4076 }
4077 }
4078 }
4079
4080 pub fn for_each<I>(&self, mut f: I)
4081 where
4082 I: FnMut(ValueOrHandler),
4083 {
4084 match &self.inner {
4085 MaybeDetached::Detached(d) => {
4086 let d = d.lock();
4087 for v in d.value.iter() {
4088 f(v.clone());
4089 }
4090 }
4091 MaybeDetached::Attached(m) => {
4092 let temp = m.with_doc_state(|state| {
4093 state
4094 .get_list_values(m.container_idx)
4095 .into_iter()
4096 .map(|value| value_to_value_or_handler(m, value))
4097 .collect::<Vec<_>>()
4098 });
4099
4100 for v in temp.into_iter() {
4101 f(v);
4102 }
4103 }
4104 }
4105 }
4106
4107 pub fn log_internal_state(&self) -> String {
4108 match &self.inner {
4109 MaybeDetached::Detached(d) => {
4110 let d = d.lock();
4111 format!("{:#?}", &d.value)
4112 }
4113 MaybeDetached::Attached(a) => a.with_state(|state| {
4114 let a = state.as_movable_list_state().unwrap();
4115 format!("{a:#?}")
4116 }),
4117 }
4118 }
4119
4120 pub fn new_detached() -> MovableListHandler {
4121 MovableListHandler {
4122 inner: MaybeDetached::new_detached(Default::default()),
4123 }
4124 }
4125
4126 pub fn get_cursor(&self, pos: usize, side: Side) -> Option<Cursor> {
4127 match &self.inner {
4128 MaybeDetached::Detached(_) => None,
4129 MaybeDetached::Attached(inner) => {
4130 let (id, len) = inner.with_state(|s| {
4131 let l = s.as_movable_list_state().unwrap();
4132 (l.get_list_item_id_at(pos), l.len())
4133 });
4134
4135 if len == 0 {
4136 return Some(Cursor {
4137 id: None,
4138 container: self.id(),
4139 side: if side == Side::Middle {
4140 Side::Left
4141 } else {
4142 side
4143 },
4144 origin_pos: 0,
4145 });
4146 }
4147
4148 if len <= pos {
4149 return Some(Cursor {
4150 id: None,
4151 container: self.id(),
4152 side: Side::Right,
4153 origin_pos: len,
4154 });
4155 }
4156
4157 let id = id?;
4158 Some(Cursor {
4159 id: Some(id.id()),
4160 container: self.id(),
4161 side,
4162 origin_pos: pos,
4163 })
4164 }
4165 }
4166 }
4167
4168 pub(crate) fn op_pos_to_user_pos(&self, new_pos: usize) -> usize {
4169 match &self.inner {
4170 MaybeDetached::Detached(_) => new_pos,
4171 MaybeDetached::Attached(inner) => {
4172 let mut pos = new_pos;
4173 inner.with_state(|s| {
4174 let l = s.as_movable_list_state().unwrap();
4175 pos = l
4176 .convert_index(new_pos, IndexType::ForOp, IndexType::ForUser)
4177 .unwrap_or(l.len());
4178 });
4179 pos
4180 }
4181 }
4182 }
4183
4184 pub fn is_deleted(&self) -> bool {
4185 match &self.inner {
4186 MaybeDetached::Detached(_) => false,
4187 MaybeDetached::Attached(a) => a.is_deleted(),
4188 }
4189 }
4190
4191 pub fn clear(&self) -> LoroResult<()> {
4192 match &self.inner {
4193 MaybeDetached::Detached(d) => {
4194 let mut d = d.lock();
4195 d.value.clear();
4196 Ok(())
4197 }
4198 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4199 }
4200 }
4201
4202 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4203 self.delete_with_txn(txn, 0, self.len())
4204 }
4205
4206 pub fn get_creator_at(&self, pos: usize) -> Option<PeerID> {
4207 match &self.inner {
4208 MaybeDetached::Detached(_) => None,
4209 MaybeDetached::Attached(a) => {
4210 a.with_state(|state| state.as_movable_list_state().unwrap().get_creator_at(pos))
4211 }
4212 }
4213 }
4214
4215 pub fn get_last_mover_at(&self, pos: usize) -> Option<PeerID> {
4216 match &self.inner {
4217 MaybeDetached::Detached(_) => None,
4218 MaybeDetached::Attached(a) => a.with_state(|state| {
4219 state
4220 .as_movable_list_state()
4221 .unwrap()
4222 .get_last_mover_at(pos)
4223 }),
4224 }
4225 }
4226
4227 pub fn get_last_editor_at(&self, pos: usize) -> Option<PeerID> {
4228 match &self.inner {
4229 MaybeDetached::Detached(_) => None,
4230 MaybeDetached::Attached(a) => a.with_state(|state| {
4231 state
4232 .as_movable_list_state()
4233 .unwrap()
4234 .get_last_editor_at(pos)
4235 }),
4236 }
4237 }
4238}
4239
4240impl MapHandler {
4241 pub fn new_detached() -> Self {
4245 Self {
4246 inner: MaybeDetached::new_detached(Default::default()),
4247 }
4248 }
4249
4250 pub fn insert(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
4251 match &self.inner {
4252 MaybeDetached::Detached(m) => {
4253 let mut m = m.lock();
4254 let value = value.into();
4255 ensure_no_regular_container_value(&value)?;
4256 m.value.insert(key.into(), ValueOrHandler::Value(value));
4257 Ok(())
4258 }
4259 MaybeDetached::Attached(a) => {
4260 a.with_txn(|txn| self.insert_with_txn(txn, key, value.into()))
4261 }
4262 }
4263 }
4264
4265 fn insert_without_skipping(&self, key: &str, value: impl Into<LoroValue>) -> LoroResult<()> {
4267 match &self.inner {
4268 MaybeDetached::Detached(m) => {
4269 let mut m = m.lock();
4270 let value = value.into();
4271 ensure_no_regular_container_value(&value)?;
4272 m.value.insert(key.into(), ValueOrHandler::Value(value));
4273 Ok(())
4274 }
4275 MaybeDetached::Attached(a) => a.with_txn(|txn| {
4276 let this = &self;
4277 let value = value.into();
4278 ensure_no_regular_container_value(&value)?;
4279
4280 let inner = this.inner.try_attached_state()?;
4281 txn.apply_local_op(
4282 inner.container_idx,
4283 crate::op::RawOpContent::Map(crate::container::map::MapSet {
4284 key: key.into(),
4285 value: Some(value.clone()),
4286 }),
4287 EventHint::Map {
4288 key: key.into(),
4289 value: Some(value.clone()),
4290 },
4291 &inner.doc,
4292 )
4293 }),
4294 }
4295 }
4296
4297 pub fn insert_with_txn(
4298 &self,
4299 txn: &mut Transaction,
4300 key: &str,
4301 value: LoroValue,
4302 ) -> LoroResult<()> {
4303 ensure_no_regular_container_value(&value)?;
4304
4305 if self.get(key).map(|x| x == value).unwrap_or(false) {
4306 return Ok(());
4308 }
4309
4310 let inner = self.inner.try_attached_state()?;
4311 txn.apply_local_op(
4312 inner.container_idx,
4313 crate::op::RawOpContent::Map(crate::container::map::MapSet {
4314 key: key.into(),
4315 value: Some(value.clone()),
4316 }),
4317 EventHint::Map {
4318 key: key.into(),
4319 value: Some(value.clone()),
4320 },
4321 &inner.doc,
4322 )
4323 }
4324
4325 pub fn insert_container<T: HandlerTrait>(&self, key: &str, handler: T) -> LoroResult<T> {
4326 match &self.inner {
4327 MaybeDetached::Detached(m) => {
4328 let mut m = m.lock();
4329 let to_insert = handler.to_handler();
4330 m.value
4331 .insert(key.into(), ValueOrHandler::Handler(to_insert.clone()));
4332 Ok(handler)
4333 }
4334 MaybeDetached::Attached(a) => {
4335 a.with_txn(|txn| self.insert_container_with_txn(txn, key, handler))
4336 }
4337 }
4338 }
4339
4340 pub fn insert_container_with_txn<H: HandlerTrait>(
4341 &self,
4342 txn: &mut Transaction,
4343 key: &str,
4344 child: H,
4345 ) -> LoroResult<H> {
4346 let inner = self.inner.try_attached_state()?;
4347 let id = txn.next_id();
4348 let container_id = ContainerID::new_normal(id, child.kind());
4349 txn.apply_local_op(
4350 inner.container_idx,
4351 crate::op::RawOpContent::Map(crate::container::map::MapSet {
4352 key: key.into(),
4353 value: Some(LoroValue::Container(container_id.clone())),
4354 }),
4355 EventHint::Map {
4356 key: key.into(),
4357 value: Some(LoroValue::Container(container_id.clone())),
4358 },
4359 &inner.doc,
4360 )?;
4361
4362 child.attach(txn, inner, container_id)
4363 }
4364
4365 pub fn delete(&self, key: &str) -> LoroResult<()> {
4366 match &self.inner {
4367 MaybeDetached::Detached(m) => {
4368 let mut m = m.lock();
4369 m.value.remove(key);
4370 Ok(())
4371 }
4372 MaybeDetached::Attached(a) => a.with_txn(|txn| self.delete_with_txn(txn, key)),
4373 }
4374 }
4375
4376 pub fn delete_with_txn(&self, txn: &mut Transaction, key: &str) -> LoroResult<()> {
4377 let inner = self.inner.try_attached_state()?;
4378 txn.apply_local_op(
4379 inner.container_idx,
4380 crate::op::RawOpContent::Map(crate::container::map::MapSet {
4381 key: key.into(),
4382 value: None,
4383 }),
4384 EventHint::Map {
4385 key: key.into(),
4386 value: None,
4387 },
4388 &inner.doc,
4389 )
4390 }
4391
4392 pub fn for_each<I>(&self, mut f: I)
4393 where
4394 I: FnMut(&str, ValueOrHandler),
4395 {
4396 match &self.inner {
4397 MaybeDetached::Detached(m) => {
4398 let m = m.lock();
4399 for (k, v) in m.value.iter() {
4400 f(k, v.clone());
4401 }
4402 }
4403 MaybeDetached::Attached(inner) => {
4404 let temp = inner.with_doc_state(|state| {
4405 state
4406 .get_map_entries(inner.container_idx)
4407 .into_iter()
4408 .map(|(key, value)| {
4409 let translated = loro_common::translate_mergeable_marker_value(
4410 &inner.id,
4411 key.as_ref(),
4412 value,
4413 );
4414 (
4415 key.to_string(),
4416 value_to_value_or_handler(inner, translated),
4417 )
4418 })
4419 .collect::<Vec<_>>()
4420 });
4421
4422 for (k, v) in temp.into_iter() {
4423 f(&k, v.clone());
4424 }
4425 }
4426 }
4427 }
4428
4429 pub fn get_child_handler(&self, key: &str) -> LoroResult<Handler> {
4430 match &self.inner {
4431 MaybeDetached::Detached(m) => {
4432 let m = m.lock();
4433 let value = m.value.get(key).unwrap();
4434 match value {
4435 ValueOrHandler::Value(v) => Err(LoroError::ArgErr(
4436 format!("Expected Handler but found {:?}", v).into_boxed_str(),
4437 )),
4438 ValueOrHandler::Handler(h) => Ok(h.clone()),
4439 }
4440 }
4441 MaybeDetached::Attached(_) => {
4442 let Some(value) = self.get_(key) else {
4443 return Err(LoroError::ArgErr(
4444 format!("Key {key} does not exist").into_boxed_str(),
4445 ));
4446 };
4447 match value {
4448 ValueOrHandler::Handler(handler) => Ok(handler),
4449 ValueOrHandler::Value(value) => Err(LoroError::ArgErr(
4450 format!("Expected Handler but found {:?}", value).into_boxed_str(),
4451 )),
4452 }
4453 }
4454 }
4455 }
4456
4457 pub fn get_deep_value_with_id(&self) -> LoroResult<LoroValue> {
4458 match &self.inner {
4459 MaybeDetached::Detached(_) => Err(LoroError::MisuseDetachedContainer {
4460 method: "get_deep_value_with_id",
4461 }),
4462 MaybeDetached::Attached(inner) => Ok(inner.with_doc_state(|state| {
4463 state.get_container_deep_value_with_id(inner.container_idx, None)
4464 })),
4465 }
4466 }
4467
4468 pub fn get(&self, key: &str) -> Option<LoroValue> {
4469 match &self.inner {
4470 MaybeDetached::Detached(m) => {
4471 let m = m.lock();
4472 m.value.get(key).map(|v| v.to_value())
4473 }
4474 MaybeDetached::Attached(inner) => {
4475 let value = inner
4476 .with_doc_state(|state| state.get_map_value_by_key(inner.container_idx, key))?;
4477 Some(loro_common::translate_mergeable_marker_value(
4478 &inner.id, key, value,
4479 ))
4480 }
4481 }
4482 }
4483
4484 pub fn get_(&self, key: &str) -> Option<ValueOrHandler> {
4486 match &self.inner {
4487 MaybeDetached::Detached(m) => {
4488 let m = m.lock();
4489 m.value.get(key).cloned()
4490 }
4491 MaybeDetached::Attached(inner) => {
4492 let value = inner
4493 .with_doc_state(|state| state.get_map_value_by_key(inner.container_idx, key))?;
4494 let value = loro_common::translate_mergeable_marker_value(&inner.id, key, value);
4495 Some(value_to_value_or_handler(inner, value))
4496 }
4497 }
4498 }
4499
4500 #[deprecated(
4507 note = "use ensure_mergeable_map/list/movable_list/text/tree/counter for lazy map-key child creation; this method creates regular op-id children"
4508 )]
4509 pub fn get_or_create_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4510 if let Some(ans) = self.get_(key) {
4511 if let ValueOrHandler::Handler(h) = ans {
4512 let kind = h.kind();
4513 return C::from_handler(h).ok_or_else(move || {
4514 LoroError::ArgErr(
4515 format!("Expected value type {} but found {:?}", child.kind(), kind)
4516 .into_boxed_str(),
4517 )
4518 });
4519 } else if let ValueOrHandler::Value(LoroValue::Null) = ans {
4520 } else {
4522 return Err(LoroError::ArgErr(
4523 format!("Expected value type {} but found {:?}", child.kind(), ans)
4524 .into_boxed_str(),
4525 ));
4526 }
4527 }
4528
4529 self.insert_container(key, child)
4530 }
4531
4532 fn ensure_mergeable_container<C: HandlerTrait>(&self, key: &str, child: C) -> LoroResult<C> {
4550 let MaybeDetached::Attached(parent) = &self.inner else {
4551 return Err(LoroError::MisuseDetachedContainer {
4552 method: "ensure_mergeable_container",
4553 });
4554 };
4555
4556 let existing_raw =
4560 parent.with_doc_state(|state| state.get_map_value_by_key(parent.container_idx, key));
4561
4562 if let Some(existing) = &existing_raw {
4567 if !matches!(existing, LoroValue::Null)
4568 && loro_common::parse_mergeable_marker(&parent.id, key, existing).is_none()
4569 {
4570 return Err(LoroError::ArgErr(
4571 format!(
4572 "Cannot create a mergeable {} at key {key:?}: the key already holds a non-mergeable value",
4573 child.kind()
4574 )
4575 .into_boxed_str(),
4576 ));
4577 }
4578 }
4579
4580 let cid = ContainerID::new_mergeable(&parent.id, key, child.kind());
4581 let marker = loro_common::mergeable_marker(&parent.id, key, child.kind());
4582
4583 if existing_raw.as_ref() != Some(&marker) {
4587 self.insert(key, marker)?;
4588 }
4589
4590 C::from_handler(create_handler(parent, cid.clone())).ok_or_else(|| {
4591 LoroError::ArgErr(
4592 format!(
4593 "Expected value type {} but found {}",
4594 child.kind(),
4595 cid.container_type()
4596 )
4597 .into_boxed_str(),
4598 )
4599 })
4600 }
4601
4602 #[cfg(feature = "counter")]
4603 pub fn ensure_mergeable_counter(&self, key: &str) -> LoroResult<counter::CounterHandler> {
4610 self.ensure_mergeable_container(key, counter::CounterHandler::new_detached())
4611 }
4612
4613 pub fn ensure_mergeable_map(&self, key: &str) -> LoroResult<MapHandler> {
4624 self.ensure_mergeable_container(key, MapHandler::new_detached())
4625 }
4626
4627 pub fn ensure_mergeable_list(&self, key: &str) -> LoroResult<ListHandler> {
4634 self.ensure_mergeable_container(key, ListHandler::new_detached())
4635 }
4636
4637 pub fn ensure_mergeable_movable_list(&self, key: &str) -> LoroResult<MovableListHandler> {
4644 self.ensure_mergeable_container(key, MovableListHandler::new_detached())
4645 }
4646
4647 pub fn ensure_mergeable_text(&self, key: &str) -> LoroResult<TextHandler> {
4654 self.ensure_mergeable_container(key, TextHandler::new_detached())
4655 }
4656
4657 pub fn ensure_mergeable_tree(&self, key: &str) -> LoroResult<TreeHandler> {
4664 self.ensure_mergeable_container(key, TreeHandler::new_detached())
4665 }
4666
4667 pub fn contains_key(&self, key: &str) -> bool {
4668 self.get(key).is_some()
4669 }
4670
4671 pub fn len(&self) -> usize {
4672 match &self.inner {
4673 MaybeDetached::Detached(m) => m.lock().value.len(),
4674 MaybeDetached::Attached(a) => {
4675 a.with_doc_state(|state| state.get_map_len(a.container_idx))
4676 }
4677 }
4678 }
4679
4680 pub fn is_empty(&self) -> bool {
4681 self.len() == 0
4682 }
4683
4684 pub fn is_deleted(&self) -> bool {
4685 match &self.inner {
4686 MaybeDetached::Detached(_) => false,
4687 MaybeDetached::Attached(a) => a.is_deleted(),
4688 }
4689 }
4690
4691 pub fn clear(&self) -> LoroResult<()> {
4692 match &self.inner {
4693 MaybeDetached::Detached(m) => {
4694 let mut m = m.lock();
4695 m.value.clear();
4696 Ok(())
4697 }
4698 MaybeDetached::Attached(a) => a.with_txn(|txn| self.clear_with_txn(txn)),
4699 }
4700 }
4701
4702 pub fn clear_with_txn(&self, txn: &mut Transaction) -> LoroResult<()> {
4703 let keys: Vec<InternalString> = self.inner.try_attached_state()?.with_state(|state| {
4704 state
4705 .as_map_state()
4706 .unwrap()
4707 .iter()
4708 .map(|(k, _)| k.clone())
4709 .collect()
4710 });
4711
4712 for key in keys {
4713 self.delete_with_txn(txn, &key)?;
4714 }
4715
4716 Ok(())
4717 }
4718
4719 pub fn keys(&self) -> impl Iterator<Item = InternalString> + '_ {
4720 let keys: Vec<InternalString> = match &self.inner {
4721 MaybeDetached::Detached(m) => {
4722 let m = m.lock();
4723 m.value.keys().map(|x| x.as_str().into()).collect()
4724 }
4725 MaybeDetached::Attached(a) => {
4726 a.with_doc_state(|state| state.get_map_keys(a.container_idx))
4727 }
4728 };
4729
4730 keys.into_iter()
4731 }
4732
4733 pub fn values(&self) -> impl Iterator<Item = ValueOrHandler> + '_ {
4734 let values: Vec<ValueOrHandler> = match &self.inner {
4735 MaybeDetached::Detached(m) => {
4736 let m = m.lock();
4737 m.value.values().cloned().collect()
4738 }
4739 MaybeDetached::Attached(a) => a.with_doc_state(|state| {
4740 state
4744 .get_map_entries(a.container_idx)
4745 .into_iter()
4746 .map(|(key, value)| {
4747 let translated = loro_common::translate_mergeable_marker_value(
4748 &a.id,
4749 key.as_ref(),
4750 value,
4751 );
4752 value_to_value_or_handler(a, translated)
4753 })
4754 .collect()
4755 }),
4756 };
4757
4758 values.into_iter()
4759 }
4760
4761 pub fn get_last_editor(&self, key: &str) -> Option<PeerID> {
4762 match &self.inner {
4763 MaybeDetached::Detached(_) => None,
4764 MaybeDetached::Attached(a) => a.with_state(|state| {
4765 let m = state.as_map_state().unwrap();
4766 m.get_last_edit_peer(key)
4767 }),
4768 }
4769 }
4770}
4771
4772fn with_txn<R>(doc: &LoroDoc, f: impl FnOnce(&mut Transaction) -> LoroResult<R>) -> LoroResult<R> {
4773 let txn = &doc.txn;
4774 let mut txn = txn.lock();
4775 loop {
4776 if let Some(txn) = &mut *txn {
4777 return f(txn);
4778 } else if cfg!(target_arch = "wasm32") || !doc.can_edit() {
4779 return Err(LoroError::AutoCommitNotStarted);
4780 } else {
4781 drop(txn);
4782 #[cfg(loom)]
4783 loom::thread::yield_now();
4784 doc.start_auto_commit();
4785 txn = doc.txn.lock();
4786 }
4787 }
4788}
4789
4790#[cfg(feature = "counter")]
4791pub mod counter {
4792
4793 use loro_common::LoroResult;
4794
4795 use crate::{
4796 txn::{EventHint, Transaction},
4797 HandlerTrait,
4798 };
4799
4800 use super::{create_handler, Handler, MaybeDetached};
4801
4802 #[derive(Clone)]
4803 pub struct CounterHandler {
4804 pub(super) inner: MaybeDetached<f64>,
4805 }
4806
4807 impl CounterHandler {
4808 pub fn new_detached() -> Self {
4809 Self {
4810 inner: MaybeDetached::new_detached(0.),
4811 }
4812 }
4813
4814 pub fn increment(&self, n: f64) -> LoroResult<()> {
4815 match &self.inner {
4816 MaybeDetached::Detached(d) => {
4817 let d = &mut d.lock().value;
4818 *d += n;
4819 Ok(())
4820 }
4821 MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, n)),
4822 }
4823 }
4824
4825 pub fn decrement(&self, n: f64) -> LoroResult<()> {
4826 match &self.inner {
4827 MaybeDetached::Detached(d) => {
4828 let d = &mut d.lock().value;
4829 *d -= n;
4830 Ok(())
4831 }
4832 MaybeDetached::Attached(a) => a.with_txn(|txn| self.increment_with_txn(txn, -n)),
4833 }
4834 }
4835
4836 fn increment_with_txn(&self, txn: &mut Transaction, n: f64) -> LoroResult<()> {
4837 let inner = self.inner.try_attached_state()?;
4838 txn.apply_local_op(
4839 inner.container_idx,
4840 crate::op::RawOpContent::Counter(n),
4841 EventHint::Counter(n),
4842 &inner.doc,
4843 )
4844 }
4845
4846 pub fn is_deleted(&self) -> bool {
4847 match &self.inner {
4848 MaybeDetached::Detached(_) => false,
4849 MaybeDetached::Attached(a) => a.is_deleted(),
4850 }
4851 }
4852
4853 pub fn clear(&self) -> LoroResult<()> {
4854 self.decrement(self.get_value().into_double().unwrap())
4855 }
4856 }
4857
4858 impl std::fmt::Debug for CounterHandler {
4859 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4860 match &self.inner {
4861 MaybeDetached::Detached(_) => write!(f, "CounterHandler Detached"),
4862 MaybeDetached::Attached(a) => write!(f, "CounterHandler {}", a.id),
4863 }
4864 }
4865 }
4866
4867 impl HandlerTrait for CounterHandler {
4868 fn is_attached(&self) -> bool {
4869 matches!(&self.inner, MaybeDetached::Attached(..))
4870 }
4871
4872 fn attached_handler(&self) -> Option<&crate::BasicHandler> {
4873 self.inner.attached_handler()
4874 }
4875
4876 fn get_value(&self) -> loro_common::LoroValue {
4877 match &self.inner {
4878 MaybeDetached::Detached(t) => {
4879 let t = t.lock();
4880 t.value.into()
4881 }
4882 MaybeDetached::Attached(a) => a.get_value(),
4883 }
4884 }
4885
4886 fn get_deep_value(&self) -> loro_common::LoroValue {
4887 self.get_value()
4888 }
4889
4890 fn kind(&self) -> loro_common::ContainerType {
4891 loro_common::ContainerType::Counter
4892 }
4893
4894 fn to_handler(&self) -> super::Handler {
4895 Handler::Counter(self.clone())
4896 }
4897
4898 fn from_handler(h: super::Handler) -> Option<Self> {
4899 match h {
4900 Handler::Counter(x) => Some(x),
4901 _ => None,
4902 }
4903 }
4904
4905 fn attach(
4906 &self,
4907 txn: &mut crate::txn::Transaction,
4908 parent: &crate::BasicHandler,
4909 self_id: loro_common::ContainerID,
4910 ) -> loro_common::LoroResult<Self> {
4911 match &self.inner {
4912 MaybeDetached::Detached(v) => {
4913 let mut v = v.lock();
4914 let inner = create_handler(parent, self_id);
4915 let c = inner.into_counter().unwrap();
4916
4917 c.increment_with_txn(txn, v.value)?;
4918
4919 v.attached = c.attached_handler().cloned();
4920 Ok(c)
4921 }
4922 MaybeDetached::Attached(a) => {
4923 let new_inner = create_handler(a, self_id);
4924 let ans = new_inner.into_counter().unwrap();
4925 let delta = *self.get_value().as_double().unwrap();
4926 ans.increment_with_txn(txn, delta)?;
4927 Ok(ans)
4928 }
4929 }
4930 }
4931
4932 fn get_attached(&self) -> Option<Self> {
4933 match &self.inner {
4934 MaybeDetached::Attached(a) => Some(Self {
4935 inner: MaybeDetached::Attached(a.clone()),
4936 }),
4937 MaybeDetached::Detached(v) => v.lock().attached.clone().map(|x| Self {
4938 inner: MaybeDetached::Attached(x),
4939 }),
4940 }
4941 }
4942
4943 fn doc(&self) -> Option<crate::LoroDoc> {
4944 match &self.inner {
4945 MaybeDetached::Detached(_) => None,
4946 MaybeDetached::Attached(a) => Some(a.doc()),
4947 }
4948 }
4949 }
4950}
4951
4952#[cfg(test)]
4953mod test {
4954 use std::borrow::Cow;
4955
4956 use super::{
4957 Handler, HandlerTrait, ListHandler, MapHandler, MovableListHandler, TextDelta, TextHandler,
4958 ValueOrHandler,
4959 };
4960 use crate::container::list::list_op::ListOp;
4961 use crate::cursor::PosType;
4962 use crate::loro::ExportMode;
4963 use crate::op::ListSlice;
4964 use crate::state::TreeParentId;
4965 use crate::txn::EventHint;
4966 use crate::version::Frontiers;
4967 use crate::LoroDoc;
4968 use crate::{fx_map, ToJson};
4969 use loro_common::{ContainerID, ContainerType, LoroError, LoroValue, ID};
4970 use serde_json::json;
4971
4972 fn recheck_fast_blob(mut bytes: Vec<u8>) -> Vec<u8> {
4973 let checksum = xxhash_rust::xxh32::xxh32(&bytes[20..], u32::from_le_bytes(*b"LORO"));
4974 bytes[16..20].copy_from_slice(&checksum.to_le_bytes());
4975 bytes
4976 }
4977
4978 fn replace_fast_snapshot_state_bytes(mut snapshot: Vec<u8>, state_bytes: &[u8]) -> Vec<u8> {
4979 let mut body = &snapshot[22..];
4980 let oplog_len = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
4981 body = &body[4 + oplog_len..];
4982 let old_state_len = u32::from_le_bytes(body[..4].try_into().unwrap()) as usize;
4983 let state_len_pos = 22 + 4 + oplog_len;
4984 let state_start = state_len_pos + 4;
4985 let state_end = state_start + old_state_len;
4986 snapshot[state_len_pos..state_start]
4987 .copy_from_slice(&(state_bytes.len() as u32).to_le_bytes());
4988 snapshot.splice(state_start..state_end, state_bytes.iter().copied());
4989 recheck_fast_blob(snapshot)
4990 }
4991
4992 fn insert_many_with_single_list_op(
4993 txn: &mut crate::txn::Transaction,
4994 list: &crate::handler::ListHandler,
4995 pos: usize,
4996 values: Vec<LoroValue>,
4997 ) {
4998 let len = values.len();
4999 let inner = list.inner.try_attached_state().unwrap();
5000 txn.apply_local_op(
5001 inner.container_idx,
5002 crate::op::RawOpContent::List(ListOp::Insert {
5003 slice: ListSlice::RawData(Cow::Owned(values)),
5004 pos,
5005 }),
5006 EventHint::InsertList {
5007 len: len as u32,
5008 pos,
5009 },
5010 &inner.doc,
5011 )
5012 .unwrap();
5013 }
5014
5015 #[test]
5016 fn richtext_handler() {
5017 let loro = LoroDoc::new();
5018 loro.set_peer_id(1).unwrap();
5019 let loro2 = LoroDoc::new();
5020 loro2.set_peer_id(2).unwrap();
5021
5022 let mut txn = loro.txn().unwrap();
5023 let text = txn.get_text("hello");
5024 text.insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
5025 .unwrap();
5026 txn.commit().unwrap();
5027 let exported = loro.export(ExportMode::all_updates()).unwrap();
5028
5029 loro2.import(&exported).unwrap();
5030 let mut txn = loro2.txn().unwrap();
5031 let text = txn.get_text("hello");
5032 assert_eq!(&**text.get_value().as_string().unwrap(), "hello");
5033 text.insert_with_txn(&mut txn, 5, " world", PosType::Unicode)
5034 .unwrap();
5035 assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
5036 txn.commit().unwrap();
5037
5038 loro.import(&loro2.export(ExportMode::all_updates()).unwrap())
5039 .unwrap();
5040 let txn = loro.txn().unwrap();
5041 let text = txn.get_text("hello");
5042 assert_eq!(&**text.get_value().as_string().unwrap(), "hello world");
5043 txn.commit().unwrap();
5044
5045 loro.checkout(&Frontiers::from_id(ID::new(2, 1))).unwrap();
5047 assert_eq!(&**text.get_value().as_string().unwrap(), "hello w");
5048 }
5049
5050 #[test]
5051 fn richtext_handler_concurrent() {
5052 let loro = LoroDoc::new();
5053 let mut txn = loro.txn().unwrap();
5054 let handler = loro.get_text("richtext");
5055 handler
5056 .insert_with_txn(&mut txn, 0, "hello", PosType::Unicode)
5057 .unwrap();
5058 txn.commit().unwrap();
5059 for i in 0..100 {
5060 let new_loro = LoroDoc::new();
5061 new_loro
5062 .import(&loro.export(ExportMode::all_updates()).unwrap())
5063 .unwrap();
5064 let mut txn = new_loro.txn().unwrap();
5065 let handler = new_loro.get_text("richtext");
5066 handler
5067 .insert_with_txn(&mut txn, i % 5, &i.to_string(), PosType::Unicode)
5068 .unwrap();
5069 txn.commit().unwrap();
5070 loro.import(
5071 &new_loro
5072 .export(ExportMode::updates(&loro.oplog_vv()))
5073 .unwrap(),
5074 )
5075 .unwrap();
5076 }
5077 }
5078
5079 #[test]
5080 fn cross_doc_txn_is_rejected() {
5081 let doc_a = LoroDoc::new();
5088 doc_a.set_peer_id(1).unwrap();
5089 let doc_b = LoroDoc::new();
5090 doc_b.set_peer_id(2).unwrap();
5091
5092 {
5094 let mut txn_b = doc_b.txn().unwrap();
5095 doc_b
5096 .get_text("text")
5097 .insert_with_txn(&mut txn_b, 0, "ok", PosType::Unicode)
5098 .unwrap();
5099 txn_b.commit().unwrap();
5100 }
5101 let vv_before = doc_b.oplog_vv();
5102
5103 let mut txn_a = doc_a.txn().unwrap();
5105 let text_b = doc_b.get_text("text");
5106 let insert_err = text_b
5107 .insert_with_txn(&mut txn_a, 0, "x", PosType::Unicode)
5108 .unwrap_err();
5109 assert!(matches!(insert_err, LoroError::UnmatchedContext { .. }));
5110 let delete_err = text_b
5111 .delete_with_txn(&mut txn_a, 0, 1, PosType::Unicode)
5112 .unwrap_err();
5113 assert!(matches!(delete_err, LoroError::UnmatchedContext { .. }));
5114 txn_a.commit().unwrap();
5115
5116 assert_eq!(&**text_b.get_value().as_string().unwrap(), "ok");
5118 assert_eq!(doc_b.oplog_vv(), vv_before);
5119 }
5120
5121 #[test]
5122 fn list_import_batch_stays_consistent_after_repeated_tail_splits() {
5123 let doc_a = LoroDoc::new();
5124 doc_a.set_peer_id(1).unwrap();
5125 let mut txn = doc_a.txn().unwrap();
5126 let list_a = txn.get_list("list");
5127 insert_many_with_single_list_op(
5128 &mut txn,
5129 &list_a,
5130 0,
5131 (0..300).map(|i| LoroValue::I64(i)).collect(),
5132 );
5133 txn.commit().unwrap();
5134
5135 let doc_b = LoroDoc::new();
5136 doc_b.set_peer_id(2).unwrap();
5137 doc_b
5138 .import(&doc_a.export(ExportMode::all_updates()).unwrap())
5139 .unwrap();
5140
5141 let list_b = doc_b.get_list("list");
5142 let mut vv = doc_a.oplog_vv();
5143 let mut updates = Vec::new();
5144 for (i, pos) in [100, 201, 252, 278].into_iter().enumerate() {
5145 list_b.insert(pos, 1000 + i as i64).unwrap();
5146 updates.push(doc_b.export(ExportMode::updates(&vv)).unwrap());
5147 vv = doc_b.oplog_vv();
5148 }
5149
5150 doc_a.import_batch(&updates).unwrap();
5151 doc_a.check_state_diff_calc_consistency_slow();
5152 doc_b.check_state_diff_calc_consistency_slow();
5153 assert_eq!(doc_a.get_deep_value(), doc_b.get_deep_value());
5154 }
5155
5156 #[test]
5157 fn richtext_handler_mark() {
5158 let loro = LoroDoc::new_auto_commit();
5159 let handler = loro.get_text("richtext");
5160 handler.insert(0, "hello world", PosType::Unicode).unwrap();
5161 handler
5162 .mark(0, 5, "bold", true.into(), PosType::Event)
5163 .unwrap();
5164 loro.commit_then_renew();
5165
5166 let value = handler.get_richtext_value();
5168 assert_eq!(value[0]["insert"], "hello".into());
5169 let meta = value[0]["attributes"].as_map().unwrap();
5170 assert_eq!(meta.len(), 1);
5171 meta.get("bold").unwrap();
5172
5173 let loro2 = LoroDoc::new_auto_commit();
5174 loro2
5175 .import(&loro.export(ExportMode::all_updates()).unwrap())
5176 .unwrap();
5177 let handler2 = loro2.get_text("richtext");
5178 assert_eq!(&**handler2.get_value().as_string().unwrap(), "hello world");
5179
5180 let value = handler2.get_richtext_value();
5182 assert_eq!(value[0]["insert"], "hello".into());
5183 let meta = value[0]["attributes"].as_map().unwrap();
5184 assert_eq!(meta.len(), 1);
5185 meta.get("bold").unwrap();
5186
5187 {
5189 handler2.insert(5, " new", PosType::Unicode).unwrap();
5190 let value = handler2.get_richtext_value();
5191 assert_eq!(
5192 value.to_json_value(),
5193 serde_json::json!([
5194 {"insert": "hello new", "attributes": {"bold": true}},
5195 {"insert": " world"}
5196 ])
5197 );
5198 }
5199 }
5200
5201 #[test]
5202 fn richtext_snapshot() {
5203 let loro = LoroDoc::new();
5204 let mut txn = loro.txn().unwrap();
5205 let handler = loro.get_text("richtext");
5206 handler
5207 .insert_with_txn(&mut txn, 0, "hello world", PosType::Unicode)
5208 .unwrap();
5209 handler
5210 .mark_with_txn(&mut txn, 0, 5, "bold", true.into(), PosType::Event)
5211 .unwrap();
5212 txn.commit().unwrap();
5213
5214 let loro2 = LoroDoc::new();
5215 loro2
5216 .import(&loro.export(ExportMode::snapshot()).unwrap())
5217 .unwrap();
5218 let handler2 = loro2.get_text("richtext");
5219 assert_eq!(
5220 handler2.get_richtext_value().to_json_value(),
5221 serde_json::json!([
5222 {"insert": "hello", "attributes": {"bold": true}},
5223 {"insert": " world"}
5224 ])
5225 );
5226 }
5227
5228 #[test]
5229 fn text_snapshot_string_queries_do_not_decode_state() {
5230 let loro = LoroDoc::new_auto_commit();
5231 let text = loro.get_text("text");
5232 text.insert(0, "a😀文", PosType::Unicode).unwrap();
5233 text.mark(1, 3, "bold", true.into(), PosType::Unicode)
5234 .unwrap();
5235
5236 let restored = LoroDoc::new();
5237 restored
5238 .import(&loro.export(ExportMode::snapshot()).unwrap())
5239 .unwrap();
5240 let text = restored.get_text("text");
5241 assert!(!text.attached_handler().unwrap().has_decoded_state());
5242
5243 assert_eq!(text.len_unicode(), 3);
5244 assert_eq!(text.len_utf16(), 4);
5245 assert_eq!(text.len_utf8(), "a😀文".len());
5246 assert_eq!(text.char_at(1, PosType::Unicode).unwrap(), '😀');
5247 assert_eq!(text.slice(1, 3, PosType::Unicode).unwrap(), "😀文");
5248 assert_eq!(
5249 text.convert_pos(2, PosType::Unicode, PosType::Utf16),
5250 Some(3)
5251 );
5252 assert!(matches!(
5253 text.delete_utf16(2, 1),
5254 Err(LoroError::UTF16InUnicodeCodePoint { pos: 2 })
5255 ));
5256 assert!(matches!(
5257 text.delete_utf8(2, 1),
5258 Err(LoroError::UTF8InUnicodeCodePoint { pos: 2 })
5259 ));
5260 assert!(matches!(
5261 text.slice_delta(2, 3, PosType::Utf16),
5262 Err(LoroError::UTF16InUnicodeCodePoint { pos: 2 })
5263 ));
5264 assert!(matches!(
5265 text.slice_delta(2, 3, PosType::Bytes),
5266 Err(LoroError::UTF8InUnicodeCodePoint { pos: 2 })
5267 ));
5268 assert!(!text.attached_handler().unwrap().has_decoded_state());
5269
5270 assert_eq!(text.get_delta().len(), 2);
5271 assert!(text.attached_handler().unwrap().has_decoded_state());
5272 }
5273
5274 #[test]
5275 fn text_lazy_event_queries_match_decoded_state() {
5276 let loro = LoroDoc::new_auto_commit();
5277 let text = loro.get_text("text");
5278 text.insert(0, "ab😀cd", PosType::Unicode).unwrap();
5279 text.mark(1, 4, "bold", true.into(), PosType::Unicode)
5280 .unwrap();
5281 text.mark(2, 3, "link", "x".into(), PosType::Unicode)
5282 .unwrap();
5283
5284 let lazy_doc = LoroDoc::new();
5285 lazy_doc
5286 .import(&loro.export(ExportMode::snapshot()).unwrap())
5287 .unwrap();
5288 let lazy_text = lazy_doc.get_text("text");
5289
5290 let decoded_doc = LoroDoc::new();
5291 decoded_doc
5292 .import(&loro.export(ExportMode::snapshot()).unwrap())
5293 .unwrap();
5294 let decoded_text = decoded_doc.get_text("text");
5295 decoded_text.get_delta();
5296
5297 assert!(!lazy_text.attached_handler().unwrap().has_decoded_state());
5298 assert!(decoded_text.attached_handler().unwrap().has_decoded_state());
5299
5300 for pos_type in [
5301 PosType::Event,
5302 PosType::Unicode,
5303 PosType::Utf16,
5304 PosType::Bytes,
5305 ] {
5306 assert_eq!(lazy_text.len(pos_type), decoded_text.len(pos_type));
5307 for pos in 0..=decoded_text.len(pos_type) {
5308 assert_eq!(
5309 lazy_text.convert_pos(pos, pos_type, PosType::Unicode),
5310 decoded_text.convert_pos(pos, pos_type, PosType::Unicode),
5311 "convert {pos_type:?} pos {pos} to unicode"
5312 );
5313 assert_eq!(
5314 lazy_text.convert_pos(pos, pos_type, PosType::Event),
5315 decoded_text.convert_pos(pos, pos_type, PosType::Event),
5316 "convert {pos_type:?} pos {pos} to event"
5317 );
5318 if pos < decoded_text.len(pos_type) {
5319 assert_eq!(
5320 lazy_text.char_at(pos, pos_type),
5321 decoded_text.char_at(pos, pos_type),
5322 "char_at {pos_type:?} pos {pos}"
5323 );
5324 }
5325 for end in pos..=decoded_text.len(pos_type) {
5326 assert_eq!(
5327 lazy_text.slice(pos, end, pos_type),
5328 decoded_text.slice(pos, end, pos_type),
5329 "slice {pos_type:?} {pos}..{end}"
5330 );
5331 }
5332 }
5333 }
5334 }
5335
5336 #[test]
5337 fn deep_value_with_id_uses_lazy_values_for_snapshot_roots() {
5338 let loro = LoroDoc::new_auto_commit();
5339 let text = loro.get_text("text");
5340 text.insert(0, "hello", PosType::Unicode).unwrap();
5341 let map = loro.get_map("map");
5342 map.insert("key", "value").unwrap();
5343 let list = loro.get_list("list");
5344 list.push("item").unwrap();
5345
5346 let restored = LoroDoc::new();
5347 restored
5348 .import(&loro.export(ExportMode::snapshot()).unwrap())
5349 .unwrap();
5350 let text = restored.get_text("text");
5351 let map = restored.get_map("map");
5352 let list = restored.get_list("list");
5353
5354 let value = restored.get_deep_value_with_id();
5355 assert_eq!(value["text"]["value"], "hello".into());
5356 assert_eq!(value["map"]["value"]["key"], "value".into());
5357 assert_eq!(value["list"]["value"][0], "item".into());
5358 assert!(!text.attached_handler().unwrap().has_decoded_state());
5359 assert!(!map.attached_handler().unwrap().has_decoded_state());
5360 assert!(!list.attached_handler().unwrap().has_decoded_state());
5361 }
5362
5363 #[test]
5364 fn lazy_value_reads_do_not_write_stale_snapshot_after_mutation() {
5365 let loro = LoroDoc::new_auto_commit();
5366 let map = loro.get_map("map");
5367 map.insert("key", "old").unwrap();
5368 let child = map
5369 .insert_container("child", MapHandler::new_detached())
5370 .unwrap();
5371 child.insert("nested", "old").unwrap();
5372 let list = loro.get_list("list");
5373 list.push("old").unwrap();
5374 let child_list = list.push_container(ListHandler::new_detached()).unwrap();
5375 child_list.push("nested-old").unwrap();
5376
5377 let restored = LoroDoc::new();
5378 restored
5379 .import(&loro.export(ExportMode::snapshot()).unwrap())
5380 .unwrap();
5381 let map = restored.get_map("map");
5382 let list = restored.get_list("list");
5383
5384 assert_eq!(map.get("key").unwrap(), "old".into());
5385 assert_eq!(list.get(0).unwrap(), "old".into());
5386 let child = match map.get_("child").unwrap() {
5387 ValueOrHandler::Handler(handler) => handler.into_map().unwrap(),
5388 ValueOrHandler::Value(value) => panic!("expected child map, got {value:?}"),
5389 };
5390 let child_list = match list.get_(1).unwrap() {
5391 ValueOrHandler::Handler(handler) => handler.into_list().unwrap(),
5392 ValueOrHandler::Value(value) => panic!("expected child list, got {value:?}"),
5393 };
5394
5395 map.insert("key", "new").unwrap();
5396 child.insert("nested", "new").unwrap();
5397 list.delete(0, 1).unwrap();
5398 list.insert(0, "new").unwrap();
5399 child_list.delete(0, 1).unwrap();
5400 child_list.insert(0, "nested-new").unwrap();
5401 restored.commit_then_renew();
5402
5403 let roundtrip = LoroDoc::new();
5404 roundtrip
5405 .import(&restored.export(ExportMode::snapshot()).unwrap())
5406 .unwrap();
5407 assert_eq!(
5408 roundtrip.get_deep_value().to_json_value(),
5409 serde_json::json!({
5410 "map": { "key": "new", "child": { "nested": "new" } },
5411 "list": ["new", ["nested-new"]]
5412 })
5413 );
5414 }
5415
5416 #[test]
5417 fn fast_snapshot_with_trailing_bytes_is_rejected_on_import() {
5418 let loro = LoroDoc::new_auto_commit();
5419 let map = loro.get_map("map");
5420 map.insert("key", "value").unwrap();
5421 let mut snapshot = loro.export(ExportMode::snapshot()).unwrap();
5422 snapshot.push(0xff);
5423 let corrupted = recheck_fast_blob(snapshot);
5424
5425 let doc = LoroDoc::new();
5426 assert!(doc.import(&corrupted).is_err());
5427 }
5428
5429 #[test]
5430 fn fast_snapshot_with_trailing_bytes_is_rejected_by_meta_decoder() {
5431 let loro = LoroDoc::new_auto_commit();
5432 let map = loro.get_map("map");
5433 map.insert("key", "value").unwrap();
5434 let mut snapshot = loro.export(ExportMode::snapshot()).unwrap();
5435 snapshot.push(0xff);
5436 let corrupted = recheck_fast_blob(snapshot);
5437
5438 assert!(LoroDoc::decode_import_blob_meta(&corrupted, true).is_err());
5439 }
5440
5441 #[test]
5442 fn fast_snapshot_empty_sstable_meta_is_rejected_on_import() {
5443 let loro = LoroDoc::new_auto_commit();
5444 let map = loro.get_map("map");
5445 map.insert("key", "value").unwrap();
5446 let snapshot = loro.export(ExportMode::snapshot()).unwrap();
5447
5448 let mut malformed_state = Vec::new();
5449 malformed_state.extend_from_slice(b"LORO");
5450 malformed_state.push(0);
5451 malformed_state.extend_from_slice(&0u32.to_le_bytes());
5452 let checksum = xxhash_rust::xxh32::xxh32(&[], u32::from_le_bytes(*b"LORO"));
5453 malformed_state.extend_from_slice(&checksum.to_le_bytes());
5454 malformed_state.extend_from_slice(&5u32.to_le_bytes());
5455 let corrupted = replace_fast_snapshot_state_bytes(snapshot, &malformed_state);
5456
5457 let doc = LoroDoc::new();
5458 assert!(doc.import(&corrupted).is_err());
5459 }
5460
5461 #[test]
5462 fn tree_meta() {
5463 let loro = LoroDoc::new_auto_commit();
5464 loro.set_peer_id(1).unwrap();
5465 let tree = loro.get_tree("root");
5466 let id = tree.create(TreeParentId::Root).unwrap();
5467 let meta = tree.get_meta(id).unwrap();
5468 meta.insert("a", 123).unwrap();
5469 loro.commit_then_renew();
5470 let meta = tree.get_meta(id).unwrap();
5471 assert_eq!(meta.get("a").unwrap(), 123.into());
5472 assert_eq!(
5473 json!([{"parent":null,"meta":{"a":123},"id":"0@1","index":0,"children":[],"fractional_index":"80"}]),
5474 tree.get_deep_value().to_json_value()
5475 );
5476 let bytes = loro.export(ExportMode::snapshot()).unwrap();
5477 let loro2 = LoroDoc::new();
5478 loro2.import(&bytes).unwrap();
5479 }
5480
5481 #[test]
5482 fn tree_meta_event() {
5483 use std::sync::Arc;
5484 let loro = LoroDoc::new_auto_commit();
5485 let tree = loro.get_tree("root");
5486 let text = loro.get_text("text");
5487
5488 let id = tree.create(TreeParentId::Root).unwrap();
5489 let meta = tree.get_meta(id).unwrap();
5490 meta.insert("a", 1).unwrap();
5491 text.insert(0, "abc", PosType::Unicode).unwrap();
5492 let _id2 = tree.create(TreeParentId::Root).unwrap();
5493 meta.insert("b", 2).unwrap();
5494
5495 let loro2 = LoroDoc::new_auto_commit();
5496 let _g = loro2.subscribe_root(Arc::new(|e| {
5497 println!("{} {:?} ", e.event_meta.by, e.event_meta.diff)
5498 }));
5499 loro2
5500 .import(&loro.export(ExportMode::all_updates()).unwrap())
5501 .unwrap();
5502 assert_eq!(loro.get_deep_value(), loro2.get_deep_value());
5503 }
5504
5505 #[test]
5506 fn richtext_apply_delta() {
5507 let loro = LoroDoc::new_auto_commit();
5508 let text = loro.get_text("text");
5509 text.apply_delta(&[TextDelta::Insert {
5510 insert: "Hello World!".into(),
5511 attributes: None,
5512 }])
5513 .unwrap();
5514 dbg!(text.get_richtext_value());
5515 text.apply_delta(&[
5516 TextDelta::Retain {
5517 retain: 6,
5518 attributes: Some(fx_map!("italic".into() => loro_common::LoroValue::Bool(true))),
5519 },
5520 TextDelta::Insert {
5521 insert: "New ".into(),
5522 attributes: Some(fx_map!("bold".into() => loro_common::LoroValue::Bool(true))),
5523 },
5524 ])
5525 .unwrap();
5526 dbg!(text.get_richtext_value());
5527 loro.commit_then_renew();
5528 assert_eq!(
5529 text.get_richtext_value().to_json_value(),
5530 json!([
5531 {"insert": "Hello ", "attributes": {"italic": true}},
5532 {"insert": "New ", "attributes": {"bold": true}},
5533 {"insert": "World!"}
5534
5535 ])
5536 )
5537 }
5538
5539 #[test]
5540 fn richtext_apply_delta_marks_without_growth() {
5541 let loro = LoroDoc::new_auto_commit();
5542 let text = loro.get_text("text");
5543 text.insert(0, "abc", PosType::Unicode).unwrap();
5544
5545 text.apply_delta(&[TextDelta::Retain {
5546 retain: 3,
5547 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5548 }])
5549 .unwrap();
5550 loro.commit_then_renew();
5551
5552 assert_eq!(text.to_string(), "abc");
5553 assert_eq!(
5554 text.get_richtext_value().to_json_value(),
5555 json!([{"insert": "abc", "attributes": {"bold": true}}])
5556 );
5557 }
5558
5559 #[test]
5560 fn richtext_apply_delta_grows_for_mark_gap() {
5561 let loro = LoroDoc::new_auto_commit();
5562 let text = loro.get_text("text");
5563
5564 text.apply_delta(&[TextDelta::Retain {
5565 retain: 1,
5566 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5567 }])
5568 .unwrap();
5569 loro.commit_then_renew();
5570
5571 assert_eq!(text.to_string(), "\n");
5572 assert_eq!(
5573 text.get_richtext_value().to_json_value(),
5574 json!([{"insert": "\n", "attributes": {"bold": true}}])
5575 );
5576 }
5577
5578 #[test]
5579 fn richtext_apply_delta_ignores_empty_inserts() {
5580 let loro = LoroDoc::new_auto_commit();
5581 let text = loro.get_text("text");
5582 text.insert(0, "seed", PosType::Unicode).unwrap();
5583
5584 text.apply_delta(&[TextDelta::Insert {
5585 insert: "".into(),
5586 attributes: Some(fx_map!("bold".into() => LoroValue::Bool(true))),
5587 }])
5588 .unwrap();
5589 loro.commit_then_renew();
5590
5591 assert_eq!(text.to_string(), "seed");
5592 assert_eq!(
5593 text.get_richtext_value().to_json_value(),
5594 json!([{"insert": "seed"}])
5595 );
5596 }
5597
5598 #[test]
5599 fn handler_trait_dispatch_reports_attached_container_identity() {
5600 let loro = LoroDoc::new_auto_commit();
5601 let handlers = [
5602 (loro.get_text("text").to_handler(), ContainerType::Text),
5603 (loro.get_map("map").to_handler(), ContainerType::Map),
5604 (loro.get_list("list").to_handler(), ContainerType::List),
5605 (
5606 loro.get_movable_list("movable").to_handler(),
5607 ContainerType::MovableList,
5608 ),
5609 (loro.get_tree("tree").to_handler(), ContainerType::Tree),
5610 ];
5611
5612 for (handler, expected_type) in handlers {
5613 assert!(handler.is_attached());
5614 assert!(handler.attached_handler().is_some());
5615 assert!(handler.doc().is_some());
5616 assert!(handler.get_attached().is_some());
5617 assert_eq!(handler.kind(), expected_type);
5618 assert_eq!(handler.c_type(), expected_type);
5619 assert_eq!(handler.id().container_type(), expected_type);
5620 assert_eq!(
5621 Handler::from_handler(handler.clone()).unwrap().c_type(),
5622 expected_type
5623 );
5624
5625 handler.get_value();
5626 handler.get_deep_value();
5627 handler.clear().unwrap();
5628 }
5629 }
5630
5631 #[test]
5632 fn handler_trait_dispatch_reports_detached_container_identity() {
5633 let handlers = [
5634 (
5635 Handler::new_unattached(ContainerType::Text),
5636 ContainerType::Text,
5637 ),
5638 (
5639 Handler::new_unattached(ContainerType::Map),
5640 ContainerType::Map,
5641 ),
5642 (
5643 Handler::new_unattached(ContainerType::List),
5644 ContainerType::List,
5645 ),
5646 (
5647 Handler::new_unattached(ContainerType::MovableList),
5648 ContainerType::MovableList,
5649 ),
5650 (
5651 Handler::new_unattached(ContainerType::Tree),
5652 ContainerType::Tree,
5653 ),
5654 ];
5655
5656 for (handler, expected_type) in handlers {
5657 assert!(!handler.is_attached());
5658 assert!(handler.attached_handler().is_none());
5659 assert!(handler.doc().is_none());
5660 assert!(handler.get_attached().is_none());
5661 assert_eq!(handler.kind(), expected_type);
5662 assert_eq!(handler.c_type(), expected_type);
5663 assert_eq!(handler.id().container_type(), expected_type);
5664 assert_eq!(handler.idx().get_type(), expected_type);
5665 assert_eq!(
5666 Handler::from_handler(handler.clone()).unwrap().c_type(),
5667 expected_type
5668 );
5669 }
5670 }
5671
5672 #[test]
5673 fn attaching_detached_handlers_sets_parent_and_attached_back_reference() {
5674 let loro = LoroDoc::new_auto_commit();
5675
5676 let map = loro.get_map("map");
5677 let detached_text = TextHandler::new_detached();
5678 detached_text
5679 .insert(0, "detached", PosType::Unicode)
5680 .unwrap();
5681 let attached_text = map.insert_container("text", detached_text.clone()).unwrap();
5682 assert!(attached_text.is_attached());
5683 assert_eq!(attached_text.to_string(), "detached");
5684 assert_eq!(attached_text.parent().unwrap().c_type(), ContainerType::Map);
5685 assert_eq!(
5686 detached_text.get_attached().unwrap().id(),
5687 attached_text.id()
5688 );
5689
5690 let list = loro.get_list("list");
5691 let detached_map = MapHandler::new_detached();
5692 detached_map.insert("k", 1_i64).unwrap();
5693 let attached_map = list.insert_container(0, detached_map.clone()).unwrap();
5694 assert!(attached_map.is_attached());
5695 assert_eq!(attached_map.parent().unwrap().c_type(), ContainerType::List);
5696 assert_eq!(detached_map.get_attached().unwrap().id(), attached_map.id());
5697
5698 let movable = loro.get_movable_list("movable");
5699 let detached_list = ListHandler::new_detached();
5700 detached_list.push("item").unwrap();
5701 let attached_list = movable.insert_container(0, detached_list.clone()).unwrap();
5702 assert!(attached_list.is_attached());
5703 assert_eq!(
5704 attached_list.parent().unwrap().c_type(),
5705 ContainerType::MovableList
5706 );
5707 assert_eq!(
5708 detached_list.get_attached().unwrap().id(),
5709 attached_list.id()
5710 );
5711
5712 let nested = attached_map
5713 .insert_container("movable", MovableListHandler::new_detached())
5714 .unwrap();
5715 assert_eq!(nested.parent().unwrap().id(), attached_map.id());
5716 }
5717
5718 #[test]
5719 fn unknown_handler_reports_identity_without_materializing_value() {
5720 let loro = LoroDoc::new_auto_commit();
5721 let id = ContainerID::Root {
5722 name: "unknown".into(),
5723 container_type: ContainerType::Unknown(7),
5724 };
5725 let handler = Handler::new_attached(id.clone(), loro.clone());
5726 let unknown = handler.as_unknown().unwrap();
5727
5728 assert!(unknown.is_attached());
5729 assert_eq!(unknown.kind(), ContainerType::Unknown(7));
5730 assert_eq!(unknown.id(), id);
5731 assert_eq!(unknown.to_handler().c_type(), ContainerType::Unknown(7));
5732 assert!(unknown.doc().is_some());
5733 assert!(!unknown.is_deleted());
5734 assert_eq!(format!("{unknown:?}"), "UnknownHandler");
5735 assert!(unknown.get_attached().is_some());
5736 assert!(super::UnknownHandler::from_handler(handler).is_some());
5737 }
5738}