From 395371db51e800bbf9339608c53f154a48b670c9 Mon Sep 17 00:00:00 2001 From: cor Date: Mon, 23 Jun 2025 15:07:26 +0200 Subject: [PATCH] fix: use real terminal cursor for block mode Previously, block cursors were always drawn manually as part of the selection/highlight rendering, which prevented terminal cursor effects (like cursor trails) from working. This change makes block cursors behave like bar/underline cursors by using the terminal's real cursor for the primary selection. - Skip rendering primary cursor in block mode when terminal is focused - Let terminal handle primary cursor rendering for all cursor types - Simplify cursor method to return actual cursor kind when focused This allows terminal-based cursor effects to work with block cursors while maintaining proper cursor visibility when the terminal is unfocused. --- helix-term/src/ui/editor.rs | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 9343d55d4..c31475ee2 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -481,12 +481,9 @@ impl EditorView { // Special-case: cursor at end of the rope. if range.head == range.anchor && range.head == text.len_chars() { - if !selection_is_primary || (cursor_is_block && is_terminal_focused) { - // Bar and underline cursors are drawn by the terminal - // BUG: If the editor area loses focus while having a bar or - // underline cursor (eg. when a regex prompt has focus) then - // the primary cursor will be invisible. This doesn't happen - // with block cursors since we manually draw *all* cursors. + if !selection_is_primary || !is_terminal_focused { + // Primary cursor is drawn by the terminal when focused + // Secondary cursors and unfocused primary cursor are drawn manually spans.push((cursor_scope, range.head..range.head + 1)); } continue; @@ -504,17 +501,17 @@ impl EditorView { cursor_start }; spans.push((selection_scope, range.anchor..selection_end)); - // add block cursors - // skip primary cursor if terminal is unfocused - crossterm cursor is used in that case - if !selection_is_primary || (cursor_is_block && is_terminal_focused) { + // add cursors + // skip primary cursor if terminal is focused - terminal cursor is used in that case + if !selection_is_primary || !is_terminal_focused { spans.push((cursor_scope, cursor_start..range.head)); } } else { // Reverse case. let cursor_end = next_grapheme_boundary(text, range.head); - // add block cursors - // skip primary cursor if terminal is unfocused - crossterm cursor is used in that case - if !selection_is_primary || (cursor_is_block && is_terminal_focused) { + // add cursors + // skip primary cursor if terminal is focused - terminal cursor is used in that case + if !selection_is_primary || !is_terminal_focused { spans.push((cursor_scope, range.head..cursor_end)); } // non block cursors look like they exclude the cursor @@ -1592,16 +1589,14 @@ impl Component for EditorView { fn cursor(&self, _area: Rect, editor: &Editor) -> (Option, CursorKind) { match editor.cursor() { - // all block cursors are drawn manually - (pos, CursorKind::Block) => { + (pos, kind) => { if self.terminal_focused { - (pos, CursorKind::Hidden) + (pos, kind) } else { - // use crossterm cursor when terminal loses focus + // use underline cursor when terminal loses focus for visibility (pos, CursorKind::Underline) } } - cursor => cursor, } } }