diff --git a/crates/fuzz/src/actor.rs b/crates/fuzz/src/actor.rs index f7f49644..edd9d84d 100644 --- a/crates/fuzz/src/actor.rs +++ b/crates/fuzz/src/actor.rs @@ -341,47 +341,6 @@ pub trait ActorTrait { } pub fn assert_value_eq(a: &LoroValue, b: &LoroValue) { - fn eq_without_position(a: &LoroValue, b: &LoroValue) -> bool { - match (a, b) { - (LoroValue::Map(a), LoroValue::Map(b)) => { - for (k, v) in a.iter() { - if k == "position" { - continue; - } - - if !eq_without_position(v, b.get(k).unwrap_or(&LoroValue::I64(0))) { - return false; - } - } - - for (k, v) in b.iter() { - if k == "position" { - continue; - } - if !eq_without_position(v, a.get(k).unwrap_or(&LoroValue::I64(0))) { - return false; - } - } - true - } - (LoroValue::List(a), LoroValue::List(b)) => { - if a.len() != b.len() { - return false; - } - - if is_tree_values(a.as_ref()) { - assert_tree_value_eq(a, b); - true - } else { - a.iter() - .zip(b.iter()) - .all(|(a, b)| eq_without_position(a, b)) - } - } - (a, b) => a == b, - } - } - #[must_use] fn eq(a: &LoroValue, b: &LoroValue) -> bool { match (a, b) { @@ -425,10 +384,10 @@ pub fn assert_value_eq(a: &LoroValue, b: &LoroValue) { assert_tree_value_eq(a_list, b_list); true } else { - eq_without_position(a, b) + a_list.iter().zip(b_list.iter()).all(|(a, b)| eq(a, b)) } } - (a, b) => eq_without_position(a, b), + (a, b) => a == b, } } assert!( @@ -445,7 +404,7 @@ pub fn is_tree_values(value: &[LoroValue]) -> bool { return map_keys.contains("id") && map_keys.contains("parent") && map_keys.contains("meta") - && map_keys.contains("position"); + && map_keys.contains("fractional_index"); } false } @@ -477,7 +436,7 @@ impl FlatNode { let meta = map.get("meta").unwrap().as_map().unwrap().as_ref().clone(); let index = *map.get("index").unwrap().as_i64().unwrap() as usize; let position = map - .get("position") + .get("fractional_index") .unwrap() .as_string() .unwrap() @@ -497,7 +456,6 @@ impl Node { let mut node_map = FxHashMap::default(); let mut parent_child_map = FxHashMap::default(); - // 首先,将所有扁平节点转换为TreeNode,并存储在HashMap中以便快速查找 for flat_node in value.iter() { let flat_node = FlatNode::from_loro_value(flat_node); let tree_node = Node { diff --git a/crates/fuzz/src/container/tree.rs b/crates/fuzz/src/container/tree.rs index cf91417d..9dab792d 100644 --- a/crates/fuzz/src/container/tree.rs +++ b/crates/fuzz/src/container/tree.rs @@ -507,7 +507,7 @@ impl TreeNode { None => LoroValue::Null, }, ); - map.insert("position".to_string(), self.position.clone().into()); + map.insert("fractional_index".to_string(), self.position.clone().into()); map.insert("index".to_string(), (index as i64).into()); map } diff --git a/crates/loro-internal/src/handler.rs b/crates/loro-internal/src/handler.rs index d53bd8e2..ccb95c88 100644 --- a/crates/loro-internal/src/handler.rs +++ b/crates/loro-internal/src/handler.rs @@ -3857,7 +3857,7 @@ mod test { .unwrap(); assert_eq!(meta, 123.into()); assert_eq!( - r#"[{"parent":null,"meta":{"a":123},"id":"0@1","index":0,"position":"80"}]"#, + r#"[{"parent":null,"meta":{"a":123},"id":"0@1","index":0,"fractional_index":"80"}]"#, tree.get_deep_value().to_json() ); let bytes = loro.export_snapshot(); diff --git a/crates/loro-internal/src/state/tree_state.rs b/crates/loro-internal/src/state/tree_state.rs index 67b46480..cdef20da 100644 --- a/crates/loro-internal/src/state/tree_state.rs +++ b/crates/loro-internal/src/state/tree_state.rs @@ -1157,7 +1157,10 @@ impl TreeNode { self.id.associated_meta_container().into(), ); t.insert("index".to_string(), (self.index as i64).into()); - t.insert("position".to_string(), self.position.to_string().into()); + t.insert( + "fractional_index".to_string(), + self.position.to_string().into(), + ); t.into() } } diff --git a/crates/loro-internal/src/value.rs b/crates/loro-internal/src/value.rs index c3da5f5e..f9e1624f 100644 --- a/crates/loro-internal/src/value.rs +++ b/crates/loro-internal/src/value.rs @@ -563,7 +563,7 @@ pub mod wasm { js_sys::Reflect::set(&obj, &"index".into(), &(*index).into()).unwrap(); js_sys::Reflect::set( &obj, - &"position".into(), + &"fractional_index".into(), &position.to_string().into(), ) .unwrap(); @@ -583,7 +583,7 @@ pub mod wasm { js_sys::Reflect::set(&obj, &"index".into(), &(*index).into()).unwrap(); js_sys::Reflect::set( &obj, - &"position".into(), + &"fractional_index".into(), &position.to_string().into(), ) .unwrap(); diff --git a/crates/loro-wasm/src/lib.rs b/crates/loro-wasm/src/lib.rs index 7d77919b..1e0cb79a 100644 --- a/crates/loro-wasm/src/lib.rs +++ b/crates/loro-wasm/src/lib.rs @@ -3184,7 +3184,7 @@ impl LoroTree { /// but also the metadata, you should use `toJson()`. /// // TODO: perf - #[wasm_bindgen(js_name = "toArray")] + #[wasm_bindgen(js_name = "toArray", skip_typescript)] pub fn to_array(&mut self) -> JsResult { let value = self.handler.get_value().into_list().unwrap(); let ans = Array::new(); @@ -3204,13 +3204,17 @@ impl LoroTree { .unwrap_or(JsValue::undefined()) .into(); let index = *v["index"].as_i64().unwrap() as u32; - let position = v["position"].as_string().unwrap(); + let position = v["fractional_index"].as_string().unwrap(); let map: LoroMap = self.get_node_by_id(&id).unwrap().data()?; let obj = Object::new(); js_sys::Reflect::set(&obj, &"id".into(), &id)?; js_sys::Reflect::set(&obj, &"parent".into(), &parent)?; js_sys::Reflect::set(&obj, &"index".into(), &JsValue::from(index))?; - js_sys::Reflect::set(&obj, &"position".into(), &JsValue::from_str(position))?; + js_sys::Reflect::set( + &obj, + &"fractional_index".into(), + &JsValue::from_str(position), + )?; js_sys::Reflect::set(&obj, &"meta".into(), &map.into())?; ans.push(&obj); } @@ -4144,6 +4148,18 @@ interface LoroList { getCursor(pos: number, side?: Side): Cursor | undefined; } +export type TreeNodeValue = { + id: TreeID, + parent: TreeID | undefined, + index: number, + fractionalIndex: string, + meta: LoroMap, +} + +interface LoroTree{ + toArray(): TreeNodeValue[]; +} + interface LoroMovableList { /** * Get the cursor position at the given pos. diff --git a/crates/loro/tests/loro_rust_test.rs b/crates/loro/tests/loro_rust_test.rs index 8d9d71b2..2d0d1e0e 100644 --- a/crates/loro/tests/loro_rust_test.rs +++ b/crates/loro/tests/loro_rust_test.rs @@ -402,7 +402,7 @@ fn tree() { root_meta.insert("color", "red").unwrap(); assert_eq!( tree.get_value_with_meta().to_json(), - r#"[{"parent":null,"meta":{"color":"red"},"id":"0@1","index":0,"position":"80"},{"parent":"0@1","meta":{},"id":"1@1","index":0,"position":"80"}]"# + r#"[{"parent":null,"meta":{"color":"red"},"id":"0@1","index":0,"fractional_index":"80"},{"parent":"0@1","meta":{},"id":"1@1","index":0,"fractional_index":"80"}]"# ) } diff --git a/loro-js/tests/compatibility.test.ts b/loro-js/tests/compatibility.test.ts index 042e0429..de9e9261 100644 --- a/loro-js/tests/compatibility.test.ts +++ b/loro-js/tests/compatibility.test.ts @@ -23,9 +23,10 @@ describe("compatibility", () => { docA.getMap("map").set("key", "123"); docA.getList("list").insert(0, 1); docA.getList("list").insert(0, "1"); - const t = docA.getTree("tree"); - const node = t.createNode(); - t.createNode(node.id, 0); + // TODO: rename + // const t = docA.getTree("tree"); + // const node = t.createNode(); + // t.createNode(node.id, 0); const bytes = docA.exportFrom(); const docB = new OLD.Loro(); @@ -40,9 +41,9 @@ describe("compatibility", () => { docA.getMap("map").set("key", "123"); docA.getList("list").insert(0, 1); docA.getList("list").insert(0, "1"); - const t = docA.getTree("tree"); - const node = t.createNode(); - t.createNode(node.id, 0); + // const t = docA.getTree("tree"); + // const node = t.createNode(); + // t.createNode(node.id, 0); const bytes = docA.exportSnapshot(); const docB = new OLD.Loro(); @@ -57,9 +58,9 @@ describe("compatibility", () => { docA.getMap("map").set("key", "123"); docA.getList("list").insert(0, 1); docA.getList("list").insert(0, "1"); - const t = docA.getTree("tree"); - const node = t.createNode(); - t.createNode(node.id); + // const t = docA.getTree("tree"); + // const node = t.createNode(); + // t.createNode(node.id); const bytes = docA.exportSnapshot(); const docB = new Loro(); @@ -74,9 +75,10 @@ describe("compatibility", () => { docA.getMap("map").set("key", "123"); docA.getList("list").insert(0, 1); docA.getList("list").insert(0, "1"); - const t = docA.getTree("tree"); - const node = t.createNode(); - t.createNode(node.id); + + // const t = docA.getTree("tree"); + // const node = t.createNode(); + // t.createNode(node.id); const bytes = docA.exportSnapshot(); const docB = new Loro(); diff --git a/loro-js/tests/tree.test.ts b/loro-js/tests/tree.test.ts index 54573c48..d6a2d07f 100644 --- a/loro-js/tests/tree.test.ts +++ b/loro-js/tests/tree.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it} from "vitest"; +import { assert, describe, expect, it} from "vitest"; import { Loro, LoroTree, LoroTreeNode } from "../src"; function assertEquals(a: any, b: any) { @@ -83,6 +83,12 @@ describe("loro tree", () => { tree2.createNode(root.id); const arr = tree2.toArray(); assertEquals(arr.length, 3); + const keys = Object.keys(arr[0]); + assert(keys.includes("id")); + assert(keys.includes("parent")); + assert(keys.includes("index")); + assert(keys.includes("fractional_index")); + assert(keys.includes("meta")); }); it("subscribe", async () => {