@@ -203,7 +203,12 @@ pub fn ensure_grapheme_boundary_next_byte(slice: RopeSlice, byte_idx: usize) ->
203 if byte_idx == 0 {
203 if byte_idx == 0 {
206+ // TODO: optimize so we're not constructing grapheme cursor twice
207+ if is_grapheme_boundary_byte(slice, byte_idx) {
206− next_grapheme_boundary_byte(slice, byte_idx - 1)
210+ next_grapheme_boundary_byte(slice, byte_idx)
@@ -235,6 +240,31 @@ pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
243+/// Returns whether the given byte position is a grapheme boundary.
245+pub fn is_grapheme_boundary_byte(slice: RopeSlice, byte_idx: usize) -> bool {
247+ debug_assert!(byte_idx <= slice.len_bytes());
249+ // Get the chunk with our byte index in it.
250+ let (chunk, chunk_byte_idx, _, _) = slice.chunk_at_byte(byte_idx);
252+ // Set up the grapheme cursor.
253+ let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true);
255+ // Determine if the given position is a grapheme cluster boundary.
257+ match gc.is_boundary(chunk, chunk_byte_idx) {
259+ Err(GraphemeIncomplete::PreContext(n)) => {
260+ let (ctx_chunk, ctx_byte_start, _, _) = slice.chunk_at_byte(n - 1);
261+ gc.provide_context(ctx_chunk, ctx_byte_start);
263+ Err(_) => unreachable!(),
238/// An iterator over the graphemes of a `RopeSlice`.
268/// An iterator over the graphemes of a `RopeSlice`.
239#[derive(Clone)]
269#[derive(Clone)]
240pub struct RopeGraphemes<'a> {
270pub struct RopeGraphemes<'a> {