on master help sign in
Speed up ensure_next_boundary during render

This code: let start = ensure_grapheme_boundary_next(text, text.byte_to_char(start)); let end = ensure_grapheme_boundary_next(text, text.byte_to_char(end)); Would convert byte to char index, but then internally immediately convert back to byte index, operate on it, then convert it to char index. This change reduces the amount of time spent in ensure_grapheme_boundary from 29% to 2%.

commit11c3ba935041437d11e1105dfa37480f2d27a6c4 authorBlaž Hrastnik <blaz@mxxn.io> dateWed 12 Jan 2022, 10:22 (5 years ago) parent9d41113
2 files changed+61−3
helix-core/src/graphemes.rs+56 helix-term/src/ui/editor.rs+5−3
helix-core/src/graphemes.rs+56
@@ -120,6 +120,43 @@ pub fn nth_next_grapheme_boundary(slice: RopeSlice, char_idx: usize, n: usize) -
120120 chunk_char_idx + tmp
121121}
122122
123+#[must_use]
124+pub fn nth_next_grapheme_boundary_byte(slice: RopeSlice, mut byte_idx: usize, n: usize) -> usize {
125+ // Bounds check
126+ debug_assert!(byte_idx <= slice.len_bytes());
127+
128+ // Get the chunk with our byte index in it.
129+ let (mut chunk, mut chunk_byte_idx, mut _chunk_char_idx, _) = slice.chunk_at_byte(byte_idx);
130+
131+ // Set up the grapheme cursor.
132+ let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true);
133+
134+ // Find the nth next grapheme cluster boundary.
135+ for _ in 0..n {
136+ loop {
137+ match gc.next_boundary(chunk, chunk_byte_idx) {
138+ Ok(None) => return slice.len_bytes(),
139+ Ok(Some(n)) => {
140+ byte_idx = n;
141+ break;
142+ }
143+ Err(GraphemeIncomplete::NextChunk) => {
144+ chunk_byte_idx += chunk.len();
145+ let (a, _, _c, _) = slice.chunk_at_byte(chunk_byte_idx);
146+ chunk = a;
147+ // chunk_char_idx = c;
148+ }
149+ Err(GraphemeIncomplete::PreContext(n)) => {
150+ let ctx_chunk = slice.chunk_at_byte(n - 1).0;
151+ gc.provide_context(ctx_chunk, n - ctx_chunk.len());
152+ }
153+ _ => unreachable!(),
154+ }
155+ }
156+ }
157+ byte_idx
158+}
159+
123160/// Finds the next grapheme boundary after the given char position.
124161#[must_use]
125162#[inline(always)]
@@ -127,6 +164,13 @@ pub fn next_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
127164 nth_next_grapheme_boundary(slice, char_idx, 1)
128165}
129166
167+/// Finds the next grapheme boundary after the given byte position.
168+#[must_use]
169+#[inline(always)]
170+pub fn next_grapheme_boundary_byte(slice: RopeSlice, byte_idx: usize) -> usize {
171+ nth_next_grapheme_boundary_byte(slice, byte_idx, 1)
172+}
173+
130174/// Returns the passed char index if it's already a grapheme boundary,
131175/// or the next grapheme boundary char index if not.
132176#[must_use]
@@ -151,6 +195,18 @@ pub fn ensure_grapheme_boundary_prev(slice: RopeSlice, char_idx: usize) -> usize
151195 }
152196}
153197
198+/// Returns the passed byte index if it's already a grapheme boundary,
199+/// or the next grapheme boundary byte index if not.
200+#[must_use]
201+#[inline]
202+pub fn ensure_grapheme_boundary_next_byte(slice: RopeSlice, byte_idx: usize) -> usize {
203+ if byte_idx == 0 {
204+ byte_idx
205+ } else {
206+ next_grapheme_boundary_byte(slice, byte_idx - 1)
207+ }
208+}
209+
154210/// Returns whether the given char position is a grapheme boundary.
155211#[must_use]
156212pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
helix-term/src/ui/editor.rs+5−3
@@ -8,7 +8,9 @@ use crate::{
88
99use helix_core::{
1010 coords_at_pos, encoding,
11− graphemes::{ensure_grapheme_boundary_next, next_grapheme_boundary, prev_grapheme_boundary},
11+ graphemes::{
12+ ensure_grapheme_boundary_next_byte, next_grapheme_boundary, prev_grapheme_boundary,
13+ },
1214 movement::Direction,
1315 syntax::{self, HighlightEvent},
1416 unicode::segmentation::UnicodeSegmentation,
@@ -154,8 +156,8 @@ impl EditorView {
154156 .map(move |event| match event {
155157 // convert byte offsets to char offset
156158 HighlightEvent::Source { start, end } => {
157− let start = ensure_grapheme_boundary_next(text, text.byte_to_char(start));
158− let end = ensure_grapheme_boundary_next(text, text.byte_to_char(end));
159+ let start = text.byte_to_char(ensure_grapheme_boundary_next_byte(text, start));
160+ let end = text.byte_to_char(ensure_grapheme_boundary_next_byte(text, end));
159161 HighlightEvent::Source { start, end }
160162 }
161163 event => event,