aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralex-ds13 <145657253+alex-ds13@users.noreply.github.com>2025-11-26 00:16:59 +0000
committerPolesznyák Márk <contact@pml68.dev>2025-12-29 23:23:41 +0100
commita6c4ffae8f973b54897b60a1c9dd1329e5abd332 (patch)
treeba91aeeb97be03ad184bb31d6e8fe37e1587ca86
parentfix: select graphemes with multi codepoints properly (diff)
downloadiced_selection-a6c4ffae8f973b54897b60a1c9dd1329e5abd332.tar.gz
fix: graphemes with multi codepoint on multiple visual lines
- The logic for graphemes with multiple codepoints was working when they were on a single visual line, however on multiple visual lines the lines after the one that contained such grapheme weren't taking that into consideration.
-rw-r--r--src/text.rs124
-rw-r--r--src/text/rich.rs126
2 files changed, 157 insertions, 93 deletions
diff --git a/src/text.rs b/src/text.rs
index 143a78d..851e77b 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -819,60 +819,92 @@ fn highlight_line(
) -> impl Iterator<Item = (f32, f32)> + '_ {
let layout = line.layout_opt().map(Vec::as_slice).unwrap_or_default();
- layout.iter().map(move |visual_line| {
- let start = visual_line
- .glyphs
- .first()
- .map(|glyph| glyph.start)
- .unwrap_or(0);
- let end = visual_line
- .glyphs
- .last()
- .map(|glyph| glyph.end)
- .unwrap_or(0);
-
- let mut range = start.max(from)..end.min(to);
-
- let x_offset = visual_line
- .glyphs
- .first()
- .map(|glyph| glyph.x)
- .unwrap_or_default();
+ // Check for multi codepoint glyphs for each previous visual line
+ let mut previous_diff = 0;
+ let previous_lines_diff = line
+ .layout_opt()
+ .map(Vec::as_slice)
+ .unwrap_or_default()
+ .iter()
+ .enumerate()
+ .map(move |(line_nr, visual_line)| {
+ if line_nr == 0 {
+ let current_diff = previous_diff
+ + visual_line
+ .glyphs
+ .iter()
+ .fold(0, |d, g| d + g.start.abs_diff(g.end) - 1);
+ previous_diff = current_diff;
+ 0
+ } else {
+ let current_diff = previous_diff
+ + visual_line
+ .glyphs
+ .iter()
+ .fold(0, |d, g| d + g.start.abs_diff(g.end) - 1);
+ let previous_diff_temp = previous_diff;
+ previous_diff = current_diff;
+ previous_diff_temp
+ }
+ });
- if range.is_empty() {
- (x_offset, 0.0)
- } else if range.start == start && range.end == end {
- (x_offset, visual_line.w)
- } else {
- let mut x = 0.0;
- let mut width = 0.0;
- for glyph in &visual_line.glyphs {
- let glyph_count = glyph.start.abs_diff(glyph.end);
+ layout.iter().zip(previous_lines_diff).map(
+ move |(visual_line, previous_lines_diff)| {
+ let start = visual_line
+ .glyphs
+ .first()
+ .map(|glyph| glyph.start)
+ .unwrap_or(0);
+ let end = visual_line
+ .glyphs
+ .last()
+ .map(|glyph| glyph.end)
+ .unwrap_or(0);
+
+ let to = to + previous_lines_diff;
+ let mut range = start.max(from)..end.min(to);
+
+ let x_offset = visual_line
+ .glyphs
+ .first()
+ .map(|glyph| glyph.x)
+ .unwrap_or_default();
+
+ if range.is_empty() {
+ (x_offset, 0.0)
+ } else if range.start == start && range.end == end {
+ (x_offset, visual_line.w)
+ } else {
+ let mut x = 0.0;
+ let mut width = 0.0;
+ for glyph in &visual_line.glyphs {
+ let glyph_count = glyph.start.abs_diff(glyph.end);
+
+ // Check for multi codepoint glyphs before or within the range
+ if glyph_count > 1 {
+ if range.start > glyph.start {
+ range.start += glyph_count - 1;
+ range.end += glyph_count - 1;
+ } else if range.end > glyph.start {
+ range.end += glyph_count - 1;
+ }
+ }
- // Check for multi codepoint glyphs before or within the range
- if glyph_count > 1 {
if range.start > glyph.start {
- range.start += glyph_count - 1;
- range.end += glyph_count - 1;
- } else if range.end > glyph.start {
- range.end += glyph_count - 1;
+ x += glyph.w;
}
- }
- if range.start > glyph.start {
- x += glyph.w;
+ if range.start <= glyph.start && range.end > glyph.start {
+ width += glyph.w;
+ } else if range.end <= glyph.start {
+ break;
+ }
}
- if range.start <= glyph.start && range.end > glyph.start {
- width += glyph.w;
- } else if range.end <= glyph.start {
- break;
- }
+ (x_offset + x, width)
}
-
- (x_offset + x, width)
- }
- })
+ },
+ )
}
fn visual_lines_offset(line: usize, buffer: &cosmic_text::Buffer) -> i32 {
diff --git a/src/text/rich.rs b/src/text/rich.rs
index 597fa26..865f64a 100644
--- a/src/text/rich.rs
+++ b/src/text/rich.rs
@@ -971,60 +971,92 @@ fn highlight_line(
) -> impl Iterator<Item = (f32, f32)> + '_ {
let layout = line.layout_opt().map(Vec::as_slice).unwrap_or_default();
- layout.iter().map(move |visual_line| {
- let start = visual_line
- .glyphs
- .first()
- .map(|glyph| glyph.start)
- .unwrap_or(0);
- let end = visual_line
- .glyphs
- .last()
- .map(|glyph| glyph.end)
- .unwrap_or(0);
-
- let mut range = start.max(from)..end.min(to);
-
- let x_offset = visual_line
- .glyphs
- .first()
- .map(|glyph| glyph.x)
- .unwrap_or_default();
-
- if range.is_empty() {
- (x_offset, 0.0)
- } else if range.start == start && range.end == end {
- (x_offset, visual_line.w)
- } else {
- let mut x = 0.0;
- let mut width = 0.0;
- for glyph in &visual_line.glyphs {
- let glyph_count = glyph.start.abs_diff(glyph.end);
+ // Check for multi codepoint glyphs for each previous visual line
+ let mut previous_diff = 0;
+ let previous_lines_diff = line
+ .layout_opt()
+ .map(Vec::as_slice)
+ .unwrap_or_default()
+ .iter()
+ .enumerate()
+ .map(move |(line_nr, visual_line)| {
+ if line_nr == 0 {
+ let current_diff = previous_diff
+ + visual_line
+ .glyphs
+ .iter()
+ .fold(0, |d, g| d + g.start.abs_diff(g.end) - 1);
+ previous_diff = current_diff;
+ 0
+ } else {
+ let current_diff = previous_diff
+ + visual_line
+ .glyphs
+ .iter()
+ .fold(0, |d, g| d + g.start.abs_diff(g.end) - 1);
+ let previous_diff_temp = previous_diff;
+ previous_diff = current_diff;
+ previous_diff_temp
+ }
+ });
+
+ layout.iter().zip(previous_lines_diff).map(
+ move |(visual_line, previous_lines_diff)| {
+ let start = visual_line
+ .glyphs
+ .first()
+ .map(|glyph| glyph.start)
+ .unwrap_or(0);
+ let end = visual_line
+ .glyphs
+ .last()
+ .map(|glyph| glyph.end)
+ .unwrap_or(0);
+
+ let to = to + previous_lines_diff;
+ let mut range = start.max(from)..end.min(to);
+
+ let x_offset = visual_line
+ .glyphs
+ .first()
+ .map(|glyph| glyph.x)
+ .unwrap_or_default();
+
+ if range.is_empty() {
+ (x_offset, 0.0)
+ } else if range.start == start && range.end == end {
+ (x_offset, visual_line.w)
+ } else {
+ let mut x = 0.0;
+ let mut width = 0.0;
+ for glyph in &visual_line.glyphs {
+ let glyph_count = glyph.start.abs_diff(glyph.end);
+
+ // Check for multi codepoint glyphs before or within the range
+ if glyph_count > 1 {
+ if range.start > glyph.start {
+ range.start += glyph_count - 1;
+ range.end += glyph_count - 1;
+ } else if range.end > glyph.start {
+ range.end += glyph_count - 1;
+ }
+ }
- // Check for multi codepoint glyphs before or within the range
- if glyph_count > 1 {
if range.start > glyph.start {
- range.start += glyph_count - 1;
- range.end += glyph_count - 1;
- } else if range.end > glyph.start {
- range.end += glyph_count - 1;
+ x += glyph.w;
}
- }
- if range.start > glyph.start {
- x += glyph.w;
+ if range.start <= glyph.start && range.end > glyph.start {
+ width += glyph.w;
+ } else if range.end <= glyph.start {
+ break;
+ }
}
- if range.start <= glyph.start && range.end > glyph.start {
- width += glyph.w;
- } else if range.end <= glyph.start {
- break;
- }
+ (x_offset + x, width)
}
-
- (x_offset + x, width)
- }
- })
+ },
+ )
}
fn visual_lines_offset(line: usize, buffer: &cosmic_text::Buffer) -> i32 {