on master help sign in
Speed up ensure_next_boundary during render
11c3ba9Blaž Hrastnik
helix-core/src/graphemes.rs+56
@@ -120,6 +120,43 @@ pub fn nth_next_grapheme_boundary(slice: RopeSlice, char_idx: usize, n: usize) -
120 chunk_char_idx + tmp
120 chunk_char_idx + tmp
121}
121}
122
122
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+
123/// Finds the next grapheme boundary after the given char position.
160/// Finds the next grapheme boundary after the given char position.
124#[must_use]
161#[must_use]
125#[inline(always)]
162#[inline(always)]
@@ -127,6 +164,13 @@ pub fn next_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
127 nth_next_grapheme_boundary(slice, char_idx, 1)
164 nth_next_grapheme_boundary(slice, char_idx, 1)
128}
165}
129
166
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+
130/// Returns the passed char index if it's already a grapheme boundary,
174/// Returns the passed char index if it's already a grapheme boundary,
131/// or the next grapheme boundary char index if not.
175/// or the next grapheme boundary char index if not.
132#[must_use]
176#[must_use]
@@ -151,6 +195,18 @@ pub fn ensure_grapheme_boundary_prev(slice: RopeSlice, char_idx: usize) -> usize
151 }
195 }
152}
196}
153
197
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+
154/// Returns whether the given char position is a grapheme boundary.
210/// Returns whether the given char position is a grapheme boundary.
155#[must_use]
211#[must_use]
156pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
212pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
helix-term/src/ui/editor.rs+5−3
@@ -8,7 +8,9 @@ use crate::{
8
8
9use helix_core::{
9use helix_core::{
10 coords_at_pos, encoding,
10 coords_at_pos, encoding,
11+ graphemes::{
11− graphemes::{ensure_grapheme_boundary_next, next_grapheme_boundary, prev_grapheme_boundary},
12+ ensure_grapheme_boundary_next_byte, next_grapheme_boundary, prev_grapheme_boundary,
13+ },
12 movement::Direction,
14 movement::Direction,
13 syntax::{self, HighlightEvent},
15 syntax::{self, HighlightEvent},
14 unicode::segmentation::UnicodeSegmentation,
16 unicode::segmentation::UnicodeSegmentation,
@@ -154,8 +156,8 @@ impl EditorView {
154 .map(move |event| match event {
156 .map(move |event| match event {
155 // convert byte offsets to char offset
157 // convert byte offsets to char offset
156 HighlightEvent::Source { start, end } => {
158 HighlightEvent::Source { start, end } => {
157− let start = ensure_grapheme_boundary_next(text, text.byte_to_char(start));
159+ let start = text.byte_to_char(ensure_grapheme_boundary_next_byte(text, start));
158− let end = ensure_grapheme_boundary_next(text, text.byte_to_char(end));
160+ let end = text.byte_to_char(ensure_grapheme_boundary_next_byte(text, end));
159 HighlightEvent::Source { start, end }
161 HighlightEvent::Source { start, end }
160 }
162 }
161 event => event,
163 event => event,