on master help sign in
fix: ensure_grapheme_boundary_next_byte needs to index at valid char
commit2302869836eedf6bba718815fbff3a5fd7afee36 authorBlaž Hrastnik <blaz@mxxn.io> dateFri 14 Jan 2022, 12:13 (5 years ago) parentadd3be8
1 file changed+31−1
helix-core/src/graphemes.rs+31−1
helix-core/src/graphemes.rs+31−1
@@ -203,7 +203,12 @@ pub fn ensure_grapheme_boundary_next_byte(slice: RopeSlice, byte_idx: usize) ->
203203 if byte_idx == 0 {
204204 byte_idx
205205 } else {
206− next_grapheme_boundary_byte(slice, byte_idx - 1)
206+ // TODO: optimize so we're not constructing grapheme cursor twice
207+ if is_grapheme_boundary_byte(slice, byte_idx) {
208+ byte_idx
209+ } else {
210+ next_grapheme_boundary_byte(slice, byte_idx)
211+ }
207212 }
208213}
209214
@@ -235,6 +240,31 @@ pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
235240 }
236241}
237242
243+/// Returns whether the given byte position is a grapheme boundary.
244+#[must_use]
245+pub fn is_grapheme_boundary_byte(slice: RopeSlice, byte_idx: usize) -> bool {
246+ // Bounds check
247+ debug_assert!(byte_idx <= slice.len_bytes());
248+
249+ // Get the chunk with our byte index in it.
250+ let (chunk, chunk_byte_idx, _, _) = slice.chunk_at_byte(byte_idx);
251+
252+ // Set up the grapheme cursor.
253+ let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true);
254+
255+ // Determine if the given position is a grapheme cluster boundary.
256+ loop {
257+ match gc.is_boundary(chunk, chunk_byte_idx) {
258+ Ok(n) => return n,
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);
262+ }
263+ Err(_) => unreachable!(),
264+ }
265+ }
266+}
267+
238268/// An iterator over the graphemes of a `RopeSlice`.
239269#[derive(Clone)]
240270pub struct RopeGraphemes<'a> {