Skip to content

Commit 2f3364a

Browse files
authored
refactor!: use better format for doc.diff (#646)
* refactor!: use better format for `doc.diff` The output of `.diff` should be directly serializable * chore: fix warnings * chore: update deno doc test * fix: correct jsondiff type * chore: add changeset
1 parent 427137c commit 2f3364a

12 files changed

Lines changed: 474 additions & 272 deletions

File tree

.changeset/proud-jars-greet.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"loro-crdt": minor
3+
---
4+
5+
refactor!: use better data type for doc.diff #646

crates/loro-common/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,19 @@ impl ContainerID {
250250
}
251251
}
252252
}
253+
254+
const LORO_CONTAINER_ID_PREFIX: &str = "🦜:";
255+
pub fn to_loro_value_string(&self) -> String {
256+
format!("{}{}", Self::LORO_CONTAINER_ID_PREFIX, self)
257+
}
258+
259+
pub fn try_from_loro_value_string(s: &str) -> Option<Self> {
260+
if let Some(s) = s.strip_prefix(Self::LORO_CONTAINER_ID_PREFIX) {
261+
Self::try_from(s).ok()
262+
} else {
263+
None
264+
}
265+
}
253266
}
254267

255268
impl std::fmt::Debug for ContainerID {

crates/loro-internal/src/handler.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,14 @@ impl Handler {
11561156
let new_id = new_h.id();
11571157
on_container_remap(old_id, new_id);
11581158
}
1159+
Some(ValueOrHandler::Value(LoroValue::Container(old_id))) => {
1160+
let new_h = x.insert_container(
1161+
&key,
1162+
Handler::new_unattached(old_id.container_type()),
1163+
)?;
1164+
let new_id = new_h.id();
1165+
on_container_remap(old_id, new_id);
1166+
}
11591167
Some(ValueOrHandler::Value(v)) => {
11601168
x.insert_without_skipping(&key, v)?;
11611169
}
@@ -2724,8 +2732,13 @@ impl ListHandler {
27242732

27252733
for v in value.iter() {
27262734
match v {
2727-
ValueOrHandler::Value(v) => {
2728-
self.insert(index, v.clone())?;
2735+
ValueOrHandler::Value(LoroValue::Container(old_id)) => {
2736+
let new_h = self.insert_container(
2737+
index,
2738+
Handler::new_unattached(old_id.container_type()),
2739+
)?;
2740+
let new_id = new_h.id();
2741+
on_container_remap(old_id.clone(), new_id);
27292742
}
27302743
ValueOrHandler::Handler(h) => {
27312744
let old_id = h.id();
@@ -2736,6 +2749,9 @@ impl ListHandler {
27362749
let new_id = new_h.id();
27372750
on_container_remap(old_id, new_id);
27382751
}
2752+
ValueOrHandler::Value(v) => {
2753+
self.insert(index, v.clone())?;
2754+
}
27392755
}
27402756

27412757
index += 1;

crates/loro-internal/src/handler/movable_list_apply_delta.rs

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -219,53 +219,69 @@ impl MovableListHandler {
219219
) -> LoroResult<()> {
220220
for v in values.iter() {
221221
match v {
222+
ValueOrHandler::Value(LoroValue::Container(old_id)) => {
223+
self.apply_insertion(attr, context, old_id.clone())?;
224+
}
225+
ValueOrHandler::Handler(handler) => {
226+
let old_id = handler.id();
227+
self.apply_insertion(attr, context, old_id)?;
228+
}
222229
ValueOrHandler::Value(value) => {
223230
self.insert(*context.index, value.clone())?;
224231
Self::update_positions_on_insert(context.to_delete, *context.index, 1);
225232
*context.index += 1;
226233
*context.index_shift += 1;
227234
}
228-
ValueOrHandler::Handler(handler) => {
229-
let mut old_id = handler.id();
230-
if !context.to_delete.contains_key(&old_id) {
231-
while let Some(new_id) = context.container_remap.get(&old_id) {
232-
old_id = new_id.clone();
233-
if context.to_delete.contains_key(&old_id) {
234-
break;
235-
}
236-
}
237-
}
235+
}
236+
}
238237

239-
if let Some(old_index) = context.to_delete.remove(&old_id) {
240-
if old_index > *context.index {
241-
ensure_cov::notify_cov("loro_internal::handler::movable_list_apply_delta::process_replacements::mov_0");
242-
self.mov(old_index, *context.index)?;
243-
context.next_deleted.push(Reverse(old_index));
244-
*context.index += 1;
245-
*context.index_shift += 1;
246-
} else {
247-
ensure_cov::notify_cov("loro_internal::handler::movable_list_apply_delta::process_replacements::mov_1");
248-
self.mov(old_index, *context.index - 1)?;
249-
}
250-
context.deleted_indices.push(old_index);
251-
Self::update_positions_on_delete(context.to_delete, old_index);
252-
Self::update_positions_on_insert(context.to_delete, *context.index, 1);
253-
} else if !attr.from_move {
254-
// Insert a new container if not moved.
255-
let new_handler = self.insert_container(
256-
*context.index,
257-
Handler::new_unattached(old_id.container_type()),
258-
)?;
259-
let new_id = new_handler.id();
260-
context.container_remap.insert(old_id, new_id);
261-
Self::update_positions_on_insert(context.to_delete, *context.index, 1);
262-
*context.index += 1;
263-
*context.index_shift += 1;
264-
}
238+
Ok(())
239+
}
240+
241+
fn apply_insertion(
242+
&self,
243+
attr: &crate::event::ListDeltaMeta,
244+
context: &mut ReplacementContext<'_>,
245+
mut old_id: ContainerID,
246+
) -> Result<(), LoroError> {
247+
if !context.to_delete.contains_key(&old_id) {
248+
while let Some(new_id) = context.container_remap.get(&old_id) {
249+
old_id = new_id.clone();
250+
if context.to_delete.contains_key(&old_id) {
251+
break;
265252
}
266253
}
267254
}
268-
255+
if let Some(old_index) = context.to_delete.remove(&old_id) {
256+
if old_index > *context.index {
257+
ensure_cov::notify_cov(
258+
"loro_internal::handler::movable_list_apply_delta::process_replacements::mov_0",
259+
);
260+
self.mov(old_index, *context.index)?;
261+
context.next_deleted.push(Reverse(old_index));
262+
*context.index += 1;
263+
*context.index_shift += 1;
264+
} else {
265+
ensure_cov::notify_cov(
266+
"loro_internal::handler::movable_list_apply_delta::process_replacements::mov_1",
267+
);
268+
self.mov(old_index, *context.index - 1)?;
269+
}
270+
context.deleted_indices.push(old_index);
271+
Self::update_positions_on_delete(context.to_delete, old_index);
272+
Self::update_positions_on_insert(context.to_delete, *context.index, 1);
273+
} else if !attr.from_move {
274+
// Insert a new container if not moved.
275+
let new_handler = self.insert_container(
276+
*context.index,
277+
Handler::new_unattached(old_id.container_type()),
278+
)?;
279+
let new_id = new_handler.id();
280+
context.container_remap.insert(old_id, new_id);
281+
Self::update_positions_on_insert(context.to_delete, *context.index, 1);
282+
*context.index += 1;
283+
*context.index_shift += 1;
284+
}
269285
Ok(())
270286
}
271287

crates/loro-internal/src/loro.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,10 @@ impl LoroDoc {
913913
let mut ans: LoroResult<()> = Ok(());
914914
let mut missing_containers: Vec<ContainerID> = Vec::new();
915915
for (mut id, diff) in diff.into_iter() {
916+
info!(
917+
"id: {:?} diff: {:?} remap: {:?}",
918+
&id, &diff, container_remap
919+
);
916920
let mut remapped = false;
917921
while let Some(rid) = container_remap.get(&id) {
918922
remapped = true;

crates/loro-wasm/src/convert.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
21
use js_sys::{Array, Map, Object, Reflect, Uint8Array};
3-
use loro_common::{IdLp, LoroListValue, LoroMapValue, LoroValue};
2+
use loro_common::{ContainerID, IdLp, LoroListValue, LoroMapValue, LoroValue};
43
use loro_delta::{array_vec, DeltaRopeBuilder};
54
use loro_internal::delta::{ResolvedMapDelta, ResolvedMapValue};
65
use loro_internal::encoding::{ImportBlobMetadata, ImportStatus};
@@ -131,7 +130,7 @@ pub(crate) fn js_to_version_vector(
131130
Ok(vv)
132131
}
133132

134-
pub(crate) fn resolved_diff_to_js(value: &Diff) -> JsValue {
133+
pub(crate) fn resolved_diff_to_js(value: &Diff, for_json: bool) -> JsValue {
135134
// create a obj
136135
let obj = Object::new();
137136
match value {
@@ -148,7 +147,7 @@ pub(crate) fn resolved_diff_to_js(value: &Diff) -> JsValue {
148147
let arr = Array::new();
149148
let mut i = 0;
150149
for v in list.iter() {
151-
let (a, b) = delta_item_to_js(v.clone());
150+
let (a, b) = delta_item_to_js(v.clone(), for_json);
152151
arr.set(i as u32, a);
153152
i += 1;
154153
if let Some(b) = b {
@@ -179,8 +178,12 @@ pub(crate) fn resolved_diff_to_js(value: &Diff) -> JsValue {
179178
js_sys::Reflect::set(&obj, &JsValue::from_str("type"), &JsValue::from_str("map"))
180179
.unwrap();
181180

182-
js_sys::Reflect::set(&obj, &JsValue::from_str("updated"), &map_delta_to_js(map))
183-
.unwrap();
181+
js_sys::Reflect::set(
182+
&obj,
183+
&JsValue::from_str("updated"),
184+
&map_delta_to_js(map, for_json),
185+
)
186+
.unwrap();
184187
}
185188

186189
Diff::Counter(v) => {
@@ -239,7 +242,7 @@ pub(crate) fn js_diff_to_inner_diff(js: JsValue) -> JsResult<Diff> {
239242
}
240243
}
241244

242-
fn delta_item_to_js(item: ListDiffItem) -> (JsValue, Option<JsValue>) {
245+
fn delta_item_to_js(item: ListDiffItem, for_json: bool) -> (JsValue, Option<JsValue>) {
243246
match item {
244247
loro_internal::loro_delta::DeltaItem::Retain { len, attr: _ } => {
245248
let obj = Object::new();
@@ -264,7 +267,7 @@ fn delta_item_to_js(item: ListDiffItem) -> (JsValue, Option<JsValue>) {
264267
for (i, v) in value.into_iter().enumerate() {
265268
let value = match v {
266269
ValueOrHandler::Value(v) => convert(v),
267-
ValueOrHandler::Handler(h) => handler_to_js_value(h),
270+
ValueOrHandler::Handler(h) => handler_to_js_value(h, for_json),
268271
};
269272
arr.set(i as u32, value);
270273
}
@@ -395,13 +398,13 @@ impl From<ImportBlobMetadata> for JsImportBlobMetadata {
395398
}
396399
}
397400

398-
fn map_delta_to_js(value: &ResolvedMapDelta) -> JsValue {
401+
fn map_delta_to_js(value: &ResolvedMapDelta, for_json: bool) -> JsValue {
399402
let obj = Object::new();
400403
for (key, value) in value.updated.iter() {
401404
let value = if let Some(value) = value.value.clone() {
402405
match value {
403406
ValueOrHandler::Value(v) => convert(v),
404-
ValueOrHandler::Handler(h) => handler_to_js_value(h),
407+
ValueOrHandler::Handler(h) => handler_to_js_value(h, for_json),
405408
}
406409
} else {
407410
JsValue::null()
@@ -413,7 +416,12 @@ fn map_delta_to_js(value: &ResolvedMapDelta) -> JsValue {
413416
obj.into_js_result().unwrap()
414417
}
415418

416-
pub(crate) fn handler_to_js_value(handler: Handler) -> JsValue {
419+
pub(crate) fn handler_to_js_value(handler: Handler, for_json: bool) -> JsValue {
420+
if for_json {
421+
let cid = handler.id();
422+
return JsValue::from_str(&cid.to_loro_value_string());
423+
}
424+
417425
match handler {
418426
Handler::Text(t) => LoroText {
419427
handler: t,
@@ -601,7 +609,11 @@ pub(crate) fn js_value_to_loro_value(js: &JsValue) -> LoroValue {
601609
LoroValue::Double(n)
602610
}
603611
} else if let Some(s) = js.as_string() {
604-
LoroValue::String(s.into())
612+
if let Some(cid) = ContainerID::try_from_loro_value_string(&s) {
613+
LoroValue::Container(cid)
614+
} else {
615+
LoroValue::String(s.into())
616+
}
605617
} else if js.is_array() {
606618
let arr = Array::from(js);
607619
let mut vec = Vec::with_capacity(arr.length() as usize);

crates/loro-wasm/src/counter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl LoroCounter {
8787
/// the WASM boundary.
8888
pub fn parent(&self) -> JsContainerOrUndefined {
8989
if let Some(p) = HandlerTrait::parent(&self.handler) {
90-
handler_to_js_value(p).into()
90+
handler_to_js_value(p, false).into()
9191
} else {
9292
JsContainerOrUndefined::from(JsValue::UNDEFINED)
9393
}
@@ -112,7 +112,7 @@ impl LoroCounter {
112112
}
113113

114114
if let Some(h) = self.handler.get_attached() {
115-
handler_to_js_value(Handler::Counter(h)).into()
115+
handler_to_js_value(Handler::Counter(h), false).into()
116116
} else {
117117
JsValue::UNDEFINED.into()
118118
}

0 commit comments

Comments
 (0)