diff --git a/crates/loro-internal/src/state.rs b/crates/loro-internal/src/state.rs index 713dcd682..a7867c741 100644 --- a/crates/loro-internal/src/state.rs +++ b/crates/loro-internal/src/state.rs @@ -1089,7 +1089,7 @@ impl DocState { if let Some(id) = pos.id { match state { State::ListState(s) => s.get_index_of_id(id), - State::RichtextState(s) => s.get_index_of_id(id), + State::RichtextState(s) => s.get_event_index_of_id(id), State::MapState(_) | State::TreeState(_) => { unreachable!() } diff --git a/crates/loro-internal/src/state/richtext_state.rs b/crates/loro-internal/src/state/richtext_state.rs index c223dc7ec..cd3b47139 100644 --- a/crates/loro-internal/src/state/richtext_state.rs +++ b/crates/loro-internal/src/state/richtext_state.rs @@ -140,6 +140,47 @@ impl RichtextState { None } + + pub fn get_event_index_of_id(&self, id: ID) -> Option { + let iter: &mut dyn Iterator; + let mut a; + let mut b; + match &self.state { + LazyLoad::Src(s) => { + a = Some(s.elements.iter()); + iter = &mut *a.as_mut().unwrap(); + } + LazyLoad::Dst(s) => { + b = Some(s.iter_chunk()); + iter = &mut *b.as_mut().unwrap(); + } + } + + let mut index = 0; + for elem in iter { + let span = elem.get_id_span(); + if span.contains(id) { + match elem { + RichtextStateChunk::Text(t) => { + let event_offset = t.convert_unicode_offset_to_event_offset( + (id.counter - span.counter.start) as usize, + ); + return Some(index + event_offset); + } + RichtextStateChunk::Style { .. } => { + return Some(index); + } + } + } + + index += match elem { + RichtextStateChunk::Text(t) => t.event_len() as usize, + RichtextStateChunk::Style { .. } => 0, + }; + } + + None + } } impl Clone for RichtextState { diff --git a/loro-js/tests/richtext.test.ts b/loro-js/tests/richtext.test.ts index 4a07f4733..697792d52 100644 --- a/loro-js/tests/richtext.test.ts +++ b/loro-js/tests/richtext.test.ts @@ -267,4 +267,14 @@ describe("richtext", () => { expect(ans.update?.containerId()).toBe("cid:root-text:Text"); } }); + + it("Styles should not affect cursor pos", () => { + const doc = new Loro(); + const text = doc.getText("text"); + text.insert(0, "Hello"); + const pos3 = text.getCursor(3); + text.mark({ start: 0, end: 2 }, "bold", true); + const ans = doc.getCursorPos(pos3!); + expect(ans.offset).toBe(3); + }); });