From e5926aec749c6828e3ee7bf35ffe06e459cb8537 Mon Sep 17 00:00:00 2001 From: Hojjat Date: Thu, 26 Mar 2026 15:47:54 -0600 Subject: [PATCH] fix(hit): move cursor to the logical end of mixed-bidi line --- src/buffer.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index cedec82..2579c71 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1109,11 +1109,13 @@ impl Buffer { new_cursor.affinity = new_cursor_affinity; } None => { - if let Some(glyph) = run.glyphs.last() { - // Position at end of line - new_cursor.index = glyph.end; - new_cursor.affinity = Affinity::Before; - } + // Click was past all glyphs in this visual run. + // Use the maximum glyph.end across all glyphs. + // this is the logical end of this visual line's byte coverage, + // correct for LTR, RTL, mixed-BiDi, and wrapped paragraphs. + let run_end = run.glyphs.iter().map(|g| g.end).max().unwrap_or(0); + new_cursor.index = run_end; + new_cursor.affinity = Affinity::Before; } } @@ -1121,10 +1123,10 @@ impl Buffer { break; } else if runs.peek().is_none() && y > run.line_y { - let mut new_cursor = Cursor::new(run.line_i, 0); - if let Some(glyph) = run.glyphs.last() { - new_cursor = run.cursor_from_glyph_right(glyph); - } + // Click below the last run: place cursor at the logical end of the + // line, regardless of paragraph direction or BiDi mixing. + let new_cursor = + Cursor::new_with_affinity(run.line_i, run.text.len(), Affinity::Before); new_cursor_opt = Some(new_cursor); } }