on master help sign in
Fix highlight code splitting graphemes.

This resulted in phantom blank lines in files with CRLF line endings, but could potentially have manifested with other graphemes as well.

commit2dba228c766e5e9e4b0fe0c12d30909f51faa28e authorNathan Vegdahl <cessen@cessen.com> dateFri 25 Jun 2021, 10:13 (5 years ago) parenteb6fb63
2 files changed+29−3
helix-core/src/graphemes.rs+24 helix-term/src/ui/editor.rs+5−3
helix-core/src/graphemes.rs+24
@@ -121,6 +121,30 @@ pub fn next_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
121121 nth_next_grapheme_boundary(slice, char_idx, 1)
122122}
123123
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+
124148/// Returns whether the given char position is a grapheme boundary.
125149pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
126150 // Bounds check
helix-term/src/ui/editor.rs+5−3
@@ -8,6 +8,7 @@ use crate::{
88
99use helix_core::{
1010 coords_at_pos,
11+ graphemes::{ensure_grapheme_boundary, ensure_grapheme_boundary_byte},
1112 syntax::{self, HighlightEvent},
1213 LineEnding, Position, Range,
1314};
@@ -141,7 +142,8 @@ impl EditorView {
141142
142143 'outer: for event in highlights {
143144 match event.unwrap() {
144− HighlightEvent::HighlightStart(span) => {
145+ HighlightEvent::HighlightStart(mut span) => {
146+ span.0 = ensure_grapheme_boundary_byte(text, span.0);
145147 spans.push(span);
146148 }
147149 HighlightEvent::HighlightEnd => {
@@ -151,8 +153,8 @@ impl EditorView {
151153 // TODO: filter out spans out of viewport for now..
152154
153155 // TODO: do these before iterating
154− let start = text.byte_to_char(start);
155− let end = text.byte_to_char(end);
156+ let start = ensure_grapheme_boundary(text, text.byte_to_char(start));
157+ let end = ensure_grapheme_boundary(text, text.byte_to_char(end));
156158
157159 let text = text.slice(start..end);
158160