on master help sign in
Fix highlight code splitting graphemes.
2dba228Nathan Vegdahl
helix-core/src/graphemes.rs+24
@@ -121,6 +121,30 @@ pub fn next_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
121 nth_next_grapheme_boundary(slice, char_idx, 1)
121 nth_next_grapheme_boundary(slice, char_idx, 1)
122}
122}
123
123
124+/// Returns the passed char index if it's already a grapheme boundary,
125+/// or the next grapheme boundary char index if not.
126+pub fn ensure_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
127+ if char_idx == 0 {
128+ 0
129+ } else {
130+ next_grapheme_boundary(slice, char_idx - 1)
131+ }
132+}
133+
134+/// Returns the passed byte index if it's already a grapheme boundary,
135+/// or the next grapheme boundary byte index if not.
136+pub fn ensure_grapheme_boundary_byte(slice: RopeSlice, byte_idx: usize) -> usize {
137+ // TODO: we can avoid the byte/char conversions entirely
138+ // if we also make byte versions of the other functions.
139+ let char_idx = slice.byte_to_char(byte_idx);
140+ let fixed_char_idx = ensure_grapheme_boundary(slice, char_idx);
141+ if fixed_char_idx == char_idx {
142+ byte_idx
143+ } else {
144+ slice.char_to_byte(fixed_char_idx)
145+ }
146+}
147+
124/// Returns whether the given char position is a grapheme boundary.
148/// Returns whether the given char position is a grapheme boundary.
125pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
149pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
126 // Bounds check
150 // Bounds check
helix-term/src/ui/editor.rs+5−3
@@ -8,6 +8,7 @@ use crate::{
8
8
9use helix_core::{
9use helix_core::{
10 coords_at_pos,
10 coords_at_pos,
11+ graphemes::{ensure_grapheme_boundary, ensure_grapheme_boundary_byte},
11 syntax::{self, HighlightEvent},
12 syntax::{self, HighlightEvent},
12 LineEnding, Position, Range,
13 LineEnding, Position, Range,
13};
14};
@@ -141,7 +142,8 @@ impl EditorView {
141
142
142 'outer: for event in highlights {
143 'outer: for event in highlights {
143 match event.unwrap() {
144 match event.unwrap() {
144− HighlightEvent::HighlightStart(span) => {
145+ HighlightEvent::HighlightStart(mut span) => {
146+ span.0 = ensure_grapheme_boundary_byte(text, span.0);
145 spans.push(span);
147 spans.push(span);
146 }
148 }
147 HighlightEvent::HighlightEnd => {
149 HighlightEvent::HighlightEnd => {
@@ -151,8 +153,8 @@ impl EditorView {
151 // TODO: filter out spans out of viewport for now..
153 // TODO: filter out spans out of viewport for now..
152
154
153 // TODO: do these before iterating
155 // TODO: do these before iterating
154− let start = text.byte_to_char(start);
156+ let start = ensure_grapheme_boundary(text, text.byte_to_char(start));
155− let end = text.byte_to_char(end);
157+ let end = ensure_grapheme_boundary(text, text.byte_to_char(end));
156
158
157 let text = text.slice(start..end);
159 let text = text.slice(start..end);
158
160