From 1918cd7ac36f0ef6f92e4d9987c86893768ff3e1 Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Wed, 10 Apr 2024 12:26:12 +0800 Subject: [PATCH] refactor: rename stable pos to cursor (#317) --- .../src/container/richtext/tracker.rs | 6 +- .../src/{stable_pos.rs => cursor.rs} | 0 crates/loro-internal/src/diff_calc.rs | 4 +- crates/loro-internal/src/handler.rs | 2 +- crates/loro-internal/src/lib.rs | 2 +- crates/loro-internal/src/loro.rs | 2 +- crates/loro-internal/src/state.rs | 4 +- crates/loro-wasm/src/lib.rs | 71 +++++++++---------- crates/loro/src/lib.rs | 10 +-- crates/loro/tests/readme.rs | 2 +- loro-js/tests/richtext.test.ts | 10 ++- 11 files changed, 59 insertions(+), 54 deletions(-) rename crates/loro-internal/src/{stable_pos.rs => cursor.rs} (100%) diff --git a/crates/loro-internal/src/container/richtext/tracker.rs b/crates/loro-internal/src/container/richtext/tracker.rs index d57e595a..4d670e5a 100644 --- a/crates/loro-internal/src/container/richtext/tracker.rs +++ b/crates/loro-internal/src/container/richtext/tracker.rs @@ -5,7 +5,7 @@ use generic_btree::{ use loro_common::{Counter, HasId, HasIdSpan, IdFull, IdSpan, Lamport, PeerID, ID}; use rle::HasLength; -use crate::{stable_pos::AbsolutePosition, VersionVector}; +use crate::{cursor::AbsolutePosition, VersionVector}; use self::{crdt_rope::CrdtRope, id_to_cursor::IdToCursor}; @@ -386,9 +386,9 @@ impl Tracker { return Some(AbsolutePosition { pos: index, side: if is_activated { - crate::stable_pos::Side::Middle + crate::cursor::Side::Middle } else { - crate::stable_pos::Side::Left + crate::cursor::Side::Left }, }); } diff --git a/crates/loro-internal/src/stable_pos.rs b/crates/loro-internal/src/cursor.rs similarity index 100% rename from crates/loro-internal/src/stable_pos.rs rename to crates/loro-internal/src/cursor.rs diff --git a/crates/loro-internal/src/diff_calc.rs b/crates/loro-internal/src/diff_calc.rs index 740fae74..62fb5af1 100644 --- a/crates/loro-internal/src/diff_calc.rs +++ b/crates/loro-internal/src/diff_calc.rs @@ -18,11 +18,11 @@ use crate::{ AnchorType, CrdtRopeDelta, RichtextChunk, RichtextChunkValue, RichtextTracker, StyleOp, }, }, + cursor::AbsolutePosition, delta::{Delta, MapDelta, MapValue}, event::InternalDiff, op::{RichOp, SliceRange, SliceRanges}, span::{HasId, HasLamport}, - stable_pos::AbsolutePosition, version::Frontiers, InternalString, VersionVector, }; @@ -462,7 +462,7 @@ pub(crate) struct ListDiffCalculator { tracker: Box, } impl ListDiffCalculator { - pub(crate) fn get_id_latest_pos(&self, id: ID) -> Option { + pub(crate) fn get_id_latest_pos(&self, id: ID) -> Option { self.tracker.get_target_id_latest_index_at_new_version(id) } } diff --git a/crates/loro-internal/src/handler.rs b/crates/loro-internal/src/handler.rs index e0e11932..7eba5d9a 100644 --- a/crates/loro-internal/src/handler.rs +++ b/crates/loro-internal/src/handler.rs @@ -7,9 +7,9 @@ use crate::{ richtext::{richtext_state::PosType, RichtextState, StyleOp, TextStyleInfoFlag}, tree::tree_op::TreeOp, }, + cursor::{Cursor, Side}, delta::{DeltaItem, StyleMeta, TreeDiffItem, TreeExternalDiff}, op::ListSlice, - stable_pos::{Cursor, Side}, state::{ContainerState, State, TreeParentId}, txn::EventHint, utils::{string_slice::StringSlice, utf16::count_utf16_len}, diff --git a/crates/loro-internal/src/lib.rs b/crates/loro-internal/src/lib.rs index e48d8fd4..3b154d9a 100644 --- a/crates/loro-internal/src/lib.rs +++ b/crates/loro-internal/src/lib.rs @@ -15,10 +15,10 @@ pub use handler::{BasicHandler, HandlerTrait, ListHandler, MapHandler, TextHandl pub use loro::LoroDoc; pub use oplog::OpLog; pub use state::DocState; +pub mod cursor; pub mod loro; pub mod obs; pub mod oplog; -pub mod stable_pos; pub mod txn; pub mod change; diff --git a/crates/loro-internal/src/loro.rs b/crates/loro-internal/src/loro.rs index 530a084d..5227024a 100644 --- a/crates/loro-internal/src/loro.rs +++ b/crates/loro-internal/src/loro.rs @@ -21,6 +21,7 @@ use crate::{ idx::ContainerIdx, list::list_op::InnerListOp, richtext::config::StyleConfigMap, IntoContainerId, }, + cursor::{AbsolutePosition, CannotFindRelativePosition, Cursor, PosQueryResult}, dag::DagUtils, encoding::{ decode_snapshot, export_snapshot, parse_header_and_body, EncodeMode, ParsedHeaderAndBody, @@ -30,7 +31,6 @@ use crate::{ id::PeerID, op::InnerContent, oplog::dag::FrontiersNotIncluded, - stable_pos::{AbsolutePosition, CannotFindRelativePosition, Cursor, PosQueryResult}, version::Frontiers, HandlerTrait, InternalString, LoroError, VersionVector, }; diff --git a/crates/loro-internal/src/state.rs b/crates/loro-internal/src/state.rs index 39baf0a4..713dcd68 100644 --- a/crates/loro-internal/src/state.rs +++ b/crates/loro-internal/src/state.rs @@ -14,6 +14,7 @@ use crate::{ idx::ContainerIdx, list::list_op::ListOp, map::MapSet, richtext::config::StyleConfigMap, tree::tree_op::TreeOp, ContainerIdRaw, }, + cursor::Cursor, delta::DeltaItem, encoding::{StateSnapshotDecodeContext, StateSnapshotEncoder}, event::{Diff, EventTriggerKind, Index, InternalContainerDiff, InternalDiff}, @@ -21,7 +22,6 @@ use crate::{ handler::ValueOrHandler, id::PeerID, op::{ListSlice, Op, RawOp, RawOpContent}, - stable_pos::Cursor, txn::Transaction, version::Frontiers, ContainerDiff, ContainerType, DocDiff, InternalString, LoroValue, @@ -1095,7 +1095,7 @@ impl DocState { } } } else { - if matches!(pos.side, crate::stable_pos::Side::Left) { + if matches!(pos.side, crate::cursor::Side::Left) { return Some(0); } diff --git a/crates/loro-wasm/src/lib.rs b/crates/loro-wasm/src/lib.rs index 967b181b..ed53e12b 100644 --- a/crates/loro-wasm/src/lib.rs +++ b/crates/loro-wasm/src/lib.rs @@ -5,6 +5,7 @@ use loro_internal::{ change::Lamport, configure::{StyleConfig, StyleConfigMap}, container::{richtext::ExpandType, ContainerID}, + cursor::{self, Side}, encoding::ImportBlobMetadata, event::Index, handler::{ @@ -12,7 +13,6 @@ use loro_internal::{ }, id::{Counter, TreeID, ID}, obs::SubID, - stable_pos::{self, Side}, version::Frontiers, ContainerType, DiffEvent, HandlerTrait, LoroDoc, LoroValue, VersionVector as InternalVersionVector, @@ -136,8 +136,8 @@ extern "C" { pub type JsImportBlobMetadata; #[wasm_bindgen(typescript_type = "Side")] pub type JsSide; - #[wasm_bindgen(typescript_type = "{ update?: StablePosition, offset: number, side: Side }")] - pub type JsStablePosQueryAns; + #[wasm_bindgen(typescript_type = "{ update?: Cursor, offset: number, side: Side }")] + pub type JsCursorQueryAns; } mod observer { @@ -1135,14 +1135,14 @@ impl Loro { /// expect(ans.offset).toBe(1); /// } /// ``` - pub fn getCursorPos(&self, stable_pos: &StablePosition) -> JsResult { + pub fn getCursorPos(&self, cursor: &Cursor) -> JsResult { let ans = self .0 - .query_pos(&stable_pos.pos) + .query_pos(&cursor.pos) .map_err(|e| JsError::new(&e.to_string()))?; let obj = Object::new(); - let update = ans.update.map(|u| StablePosition { pos: u }); + let update = ans.update.map(|u| Cursor { pos: u }); if let Some(update) = update { let update_value: JsValue = update.into(); Reflect::set(&obj, &JsValue::from_str("update"), &update_value)?; @@ -1505,7 +1505,7 @@ impl LoroText { } #[wasm_bindgen(skip_typescript)] - pub fn getCursor(&self, pos: usize, side: JsSide) -> Option { + pub fn getCursor(&self, pos: usize, side: JsSide) -> Option { let mut side_value = Side::Middle; if side.is_truthy() { let num = side.as_f64().expect("Side must be -1 | 0 | 1"); @@ -1513,7 +1513,7 @@ impl LoroText { } self.handler .get_cursor(pos, side_value) - .map(|pos| StablePosition { pos }) + .map(|pos| Cursor { pos }) } } @@ -2131,7 +2131,7 @@ impl LoroList { } #[wasm_bindgen(skip_typescript)] - pub fn getCursor(&self, pos: usize, side: JsSide) -> Option { + pub fn getCursor(&self, pos: usize, side: JsSide) -> Option { let mut side_value = Side::Middle; if side.is_truthy() { let num = side.as_f64().expect("Side must be -1 | 0 | 1"); @@ -2139,7 +2139,7 @@ impl LoroList { } self.handler .get_cursor(pos, side_value) - .map(|pos| StablePosition { pos }) + .map(|pos| Cursor { pos }) } } @@ -2569,12 +2569,12 @@ impl Default for LoroTree { #[derive(Clone)] #[wasm_bindgen] -pub struct StablePosition { - pos: stable_pos::Cursor, +pub struct Cursor { + pos: cursor::Cursor, } #[wasm_bindgen] -impl StablePosition { +impl Cursor { pub fn containerId(&self) -> JsContainerID { let js_value: JsValue = self.pos.container.to_string().into(); JsContainerID::from(js_value) @@ -2592,9 +2592,9 @@ impl StablePosition { pub fn side(&self) -> JsSide { JsValue::from(match self.pos.side { - stable_pos::Side::Left => -1, - stable_pos::Side::Middle => 0, - stable_pos::Side::Right => 1, + cursor::Side::Left => -1, + cursor::Side::Middle => 0, + cursor::Side::Right => 1, }) .into() } @@ -2603,10 +2603,9 @@ impl StablePosition { self.pos.encode() } - pub fn decode(data: &[u8]) -> JsResult { - let pos = - stable_pos::Cursor::decode(data).map_err(|e| JsValue::from_str(&e.to_string()))?; - Ok(StablePosition { pos }) + pub fn decode(data: &[u8]) -> JsResult { + let pos = cursor::Cursor::decode(data).map_err(|e| JsValue::from_str(&e.to_string()))?; + Ok(Cursor { pos }) } } @@ -2940,19 +2939,19 @@ export interface ImportBlobMetadata { interface LoroText { /** - * Get a stable position representing the cursor position. + * Get the cursor position at the given pos. * * When expressing the position of a cursor, using "index" can be unstable * because the cursor's position may change due to other deletions and insertions, * requiring updates with each edit. To stably represent a position or range within * a list structure, we can utilize the ID of each item/character on List CRDT or * Text CRDT for expression. - * - * Loro optimizes State metadata by not storing the IDs of deleted elements. This - * approach complicates tracking cursors since they rely on these IDs. The solution - * recalculates position by replaying relevant history to update stable positions - * accurately. To minimize the performance impact of history replay, the system - * updates cursor info to reference only the IDs of currently present elements, + * + * Loro optimizes State metadata by not storing the IDs of deleted elements. This + * approach complicates tracking cursors since they rely on these IDs. The solution + * recalculates position by replaying relevant history to update cursors + * accurately. To minimize the performance impact of history replay, the system + * updates cursor info to reference only the IDs of currently present elements, * thereby reducing the need for replay. * * @example @@ -2973,24 +2972,24 @@ interface LoroText { * } * ``` */ - getCursor(pos: number, side?: Side): StablePosition | undefined; + getCursor(pos: number, side?: Side): Cursor | undefined; } interface LoroList { /** - * Get a stable position representing the cursor position. + * Get the cursor position at the given pos. * * When expressing the position of a cursor, using "index" can be unstable * because the cursor's position may change due to other deletions and insertions, * requiring updates with each edit. To stably represent a position or range within * a list structure, we can utilize the ID of each item/character on List CRDT or * Text CRDT for expression. - * - * Loro optimizes State metadata by not storing the IDs of deleted elements. This - * approach complicates tracking cursors since they rely on these IDs. The solution - * recalculates position by replaying relevant history to update stable positions - * accurately. To minimize the performance impact of history replay, the system - * updates cursor info to reference only the IDs of currently present elements, + * + * Loro optimizes State metadata by not storing the IDs of deleted elements. This + * approach complicates tracking cursors since they rely on these IDs. The solution + * recalculates position by replaying relevant history to update cursors + * accurately. To minimize the performance impact of history replay, the system + * updates cursor info to reference only the IDs of currently present elements, * thereby reducing the need for replay. * * @example @@ -3011,7 +3010,7 @@ interface LoroList { * } * ``` */ - getCursor(pos: number, side?: Side): StablePosition | undefined; + getCursor(pos: number, side?: Side): Cursor | undefined; } export type Side = -1 | 0 | 1; diff --git a/crates/loro/src/lib.rs b/crates/loro/src/lib.rs index f19b769e..1fb45ef1 100644 --- a/crates/loro/src/lib.rs +++ b/crates/loro/src/lib.rs @@ -3,13 +3,13 @@ use either::Either; use event::{DiffEvent, Subscriber}; use loro_internal::change::Timestamp; use loro_internal::container::IntoContainerId; +use loro_internal::cursor::CannotFindRelativePosition; +use loro_internal::cursor::Cursor; +use loro_internal::cursor::PosQueryResult; +use loro_internal::cursor::Side; use loro_internal::encoding::ImportBlobMetadata; use loro_internal::handler::HandlerTrait; use loro_internal::handler::ValueOrHandler; -use loro_internal::stable_pos::CannotFindRelativePosition; -use loro_internal::stable_pos::Cursor; -use loro_internal::stable_pos::PosQueryResult; -use loro_internal::stable_pos::Side; use loro_internal::LoroDoc as InnerLoroDoc; use loro_internal::OpLog; @@ -596,7 +596,7 @@ impl LoroList { /// /// ``` /// use loro::LoroDoc; - /// use loro_internal::stable_pos::Side; + /// use loro_internal::cursor::Side; /// /// let doc = LoroDoc::new(); /// let list = doc.get_list("list"); diff --git a/crates/loro/tests/readme.rs b/crates/loro/tests/readme.rs index ee85f4ec..09972920 100644 --- a/crates/loro/tests/readme.rs +++ b/crates/loro/tests/readme.rs @@ -49,7 +49,7 @@ fn readme_basic() { #[test] fn get_list_cursor_example() { use loro::LoroDoc; - use loro_internal::stable_pos::Side; + use loro_internal::cursor::Side; let doc = LoroDoc::new(); let list = doc.get_list("list"); diff --git a/loro-js/tests/richtext.test.ts b/loro-js/tests/richtext.test.ts index b0f6135e..40917854 100644 --- a/loro-js/tests/richtext.test.ts +++ b/loro-js/tests/richtext.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { Delta, Loro, TextDiff } from "../src"; -import { OpId, setDebug } from "loro-wasm"; +import { Cursor, OpId, setDebug } from "loro-wasm"; describe("richtext", () => { it("mark", () => { @@ -237,12 +237,18 @@ describe("richtext", () => { expect(ans.update).toBeUndefined(); } text.insert(0, "abc"); + const bytes = pos0!.encode(); + // Sending pos0 over the network + const pos0decoded = Cursor.decode(bytes); + const docA = new Loro(); + docA.import(doc.exportFrom()); { - const ans = doc.getCursorPos(pos0!); + const ans = docA.getCursorPos(pos0decoded!); expect(ans.side).toBe(0); expect(ans.offset).toBe(3); expect(ans.update).toBeUndefined(); } + // If "1" is removed from the text, the stable position should be updated text.delete(3, 1); // remove "1", "abc23" doc.commit();