From 2dba228c766e5e9e4b0fe0c12d30909f51faa28e Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Fri, 25 Jun 2021 10:13:51 -0700 Subject: [PATCH] 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. --- helix-core/src/graphemes.rs | 24 ++++++++++++++++++++++++ helix-term/src/ui/editor.rs | 8 +++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/helix-core/src/graphemes.rs b/helix-core/src/graphemes.rs index 2e6a925f2..e2f7c3f37 100644 --- a/helix-core/src/graphemes.rs +++ b/helix-core/src/graphemes.rs @@ -121,6 +121,30 @@ pub fn next_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize { 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. pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool { // Bounds check diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 93f000e74..14cfc7ddc 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -8,6 +8,7 @@ use crate::{ use helix_core::{ coords_at_pos, + graphemes::{ensure_grapheme_boundary, ensure_grapheme_boundary_byte}, syntax::{self, HighlightEvent}, LineEnding, Position, Range, }; @@ -141,7 +142,8 @@ impl EditorView { 'outer: for event in highlights { match event.unwrap() { - HighlightEvent::HighlightStart(span) => { + HighlightEvent::HighlightStart(mut span) => { + span.0 = ensure_grapheme_boundary_byte(text, span.0); spans.push(span); } HighlightEvent::HighlightEnd => { @@ -151,8 +153,8 @@ impl EditorView { // TODO: filter out spans out of viewport for now.. // TODO: do these before iterating - let start = text.byte_to_char(start); - let end = text.byte_to_char(end); + let start = ensure_grapheme_boundary(text, text.byte_to_char(start)); + let end = ensure_grapheme_boundary(text, text.byte_to_char(end)); let text = text.slice(start..end);