From 7d17fd7ba3c28c09e6bd2364279ba70036368fff Mon Sep 17 00:00:00 2001 From: cor Date: Mon, 23 Jun 2025 16:52:27 +0200 Subject: [PATCH] fix: show primary cursor when command prompt is active When the command prompt (:) is active, the terminal cursor moves to the prompt line, leaving the primary cursor position in the editor invisible. This change detects when a prompt component is active and forces the primary cursor to be drawn manually in the editor while the prompt is shown. - Add prompt_active field to EditorView to track prompt state - Update compositor to detect and communicate prompt state to EditorView - Modify cursor highlighting logic to force primary cursor drawing when prompt is active - Maintain real terminal cursor for prompt while showing fake cursor in editor --- helix-term/src/compositor.rs | 7 +++++++ helix-term/src/ui/editor.rs | 22 +++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs index 28c8651a2..b4d7c6848 100644 --- a/helix-term/src/compositor.rs +++ b/helix-term/src/compositor.rs @@ -177,7 +177,14 @@ impl Compositor { } pub fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { + // Check if there are prompt layers active and update EditorView + let has_prompt = self.has_component("helix_term::ui::prompt::Prompt"); + for layer in &mut self.layers { + // Update prompt state for EditorView + if let Some(editor_view) = layer.as_any_mut().downcast_mut::() { + editor_view.prompt_active = has_prompt; + } layer.render(area, surface, cx); } } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index facab4730..573350457 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -44,6 +44,8 @@ pub struct EditorView { spinners: ProgressSpinners, /// Tracks if the terminal window is focused by reaction to terminal focus events terminal_focused: bool, + /// Tracks if there are prompt layers active (updated by compositor) + pub prompt_active: bool, } #[derive(Debug, Clone)] @@ -67,6 +69,7 @@ impl EditorView { completion: None, spinners: ProgressSpinners::default(), terminal_focused: true, + prompt_active: false, } } @@ -133,7 +136,7 @@ impl EditorView { if let Some(tabstops) = Self::tabstop_highlights(doc, theme) { overlays.push(tabstops); } - overlays.push(Self::doc_selection_highlights( + overlays.push(self.doc_selection_highlights( editor.mode(), doc, view, @@ -427,7 +430,8 @@ impl EditorView { } /// Get highlight spans for selections in a document view. - pub fn doc_selection_highlights( + fn doc_selection_highlights( + &self, mode: Mode, doc: &Document, view: &View, @@ -481,9 +485,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 || !is_terminal_focused { - // Primary cursor is drawn by the terminal when focused - // Secondary cursors and unfocused primary cursor are drawn manually + if !selection_is_primary || !is_terminal_focused || self.prompt_active { + // Primary cursor is drawn by the terminal when focused and no prompt is active + // Secondary cursors, unfocused primary cursor, and editor cursor when prompt is active are drawn manually spans.push((cursor_scope, range.head..range.head + 1)); } continue; @@ -502,16 +506,16 @@ impl EditorView { }; spans.push((selection_scope, range.anchor..selection_end)); // add cursors - // skip primary cursor if terminal is focused - terminal cursor is used in that case - if !selection_is_primary || !is_terminal_focused { + // skip primary cursor if terminal is focused and no prompt is active - terminal cursor is used in that case + if !selection_is_primary || !is_terminal_focused || self.prompt_active { spans.push((cursor_scope, cursor_start..range.head)); } } else { // Reverse case. let cursor_end = next_grapheme_boundary(text, range.head); // add cursors - // skip primary cursor if terminal is focused - terminal cursor is used in that case - if !selection_is_primary || !is_terminal_focused { + // skip primary cursor if terminal is focused and no prompt is active - terminal cursor is used in that case + if !selection_is_primary || !is_terminal_focused || self.prompt_active { spans.push((cursor_scope, range.head..cursor_end)); } // non block cursors look like they exclude the cursor