mirror of https://github.com/helix-editor/helix
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.pull/382/head
parent
eb6fb63e74
commit
2dba228c76
|
@ -121,6 +121,30 @@ pub fn next_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
|
||||||
nth_next_grapheme_boundary(slice, char_idx, 1)
|
nth_next_grapheme_boundary(slice, char_idx, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the passed char index if it's already a grapheme boundary,
|
||||||
|
/// or the next grapheme boundary char index if not.
|
||||||
|
pub fn ensure_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
|
||||||
|
if char_idx == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
next_grapheme_boundary(slice, char_idx - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the passed byte index if it's already a grapheme boundary,
|
||||||
|
/// or the next grapheme boundary byte index if not.
|
||||||
|
pub fn ensure_grapheme_boundary_byte(slice: RopeSlice, byte_idx: usize) -> usize {
|
||||||
|
// TODO: we can avoid the byte/char conversions entirely
|
||||||
|
// if we also make byte versions of the other functions.
|
||||||
|
let char_idx = slice.byte_to_char(byte_idx);
|
||||||
|
let fixed_char_idx = ensure_grapheme_boundary(slice, char_idx);
|
||||||
|
if fixed_char_idx == char_idx {
|
||||||
|
byte_idx
|
||||||
|
} else {
|
||||||
|
slice.char_to_byte(fixed_char_idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether the given char position is a grapheme boundary.
|
/// Returns whether the given char position is a grapheme boundary.
|
||||||
pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
|
pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
|
||||||
// Bounds check
|
// Bounds check
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
|
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
coords_at_pos,
|
coords_at_pos,
|
||||||
|
graphemes::{ensure_grapheme_boundary, ensure_grapheme_boundary_byte},
|
||||||
syntax::{self, HighlightEvent},
|
syntax::{self, HighlightEvent},
|
||||||
LineEnding, Position, Range,
|
LineEnding, Position, Range,
|
||||||
};
|
};
|
||||||
|
@ -141,7 +142,8 @@ impl EditorView {
|
||||||
|
|
||||||
'outer: for event in highlights {
|
'outer: for event in highlights {
|
||||||
match event.unwrap() {
|
match event.unwrap() {
|
||||||
HighlightEvent::HighlightStart(span) => {
|
HighlightEvent::HighlightStart(mut span) => {
|
||||||
|
span.0 = ensure_grapheme_boundary_byte(text, span.0);
|
||||||
spans.push(span);
|
spans.push(span);
|
||||||
}
|
}
|
||||||
HighlightEvent::HighlightEnd => {
|
HighlightEvent::HighlightEnd => {
|
||||||
|
@ -151,8 +153,8 @@ impl EditorView {
|
||||||
// TODO: filter out spans out of viewport for now..
|
// TODO: filter out spans out of viewport for now..
|
||||||
|
|
||||||
// TODO: do these before iterating
|
// TODO: do these before iterating
|
||||||
let start = text.byte_to_char(start);
|
let start = ensure_grapheme_boundary(text, text.byte_to_char(start));
|
||||||
let end = text.byte_to_char(end);
|
let end = ensure_grapheme_boundary(text, text.byte_to_char(end));
|
||||||
|
|
||||||
let text = text.slice(start..end);
|
let text = text.slice(start..end);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue