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
pull/13821/head
cor 2025-06-23 16:52:27 +02:00
parent 5af123a4d9
commit 7d17fd7ba3
No known key found for this signature in database
2 changed files with 20 additions and 9 deletions

View File

@ -177,7 +177,14 @@ impl Compositor {
} }
pub fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { 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 { for layer in &mut self.layers {
// Update prompt state for EditorView
if let Some(editor_view) = layer.as_any_mut().downcast_mut::<crate::ui::EditorView>() {
editor_view.prompt_active = has_prompt;
}
layer.render(area, surface, cx); layer.render(area, surface, cx);
} }
} }

View File

@ -44,6 +44,8 @@ pub struct EditorView {
spinners: ProgressSpinners, spinners: ProgressSpinners,
/// Tracks if the terminal window is focused by reaction to terminal focus events /// Tracks if the terminal window is focused by reaction to terminal focus events
terminal_focused: bool, terminal_focused: bool,
/// Tracks if there are prompt layers active (updated by compositor)
pub prompt_active: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -67,6 +69,7 @@ impl EditorView {
completion: None, completion: None,
spinners: ProgressSpinners::default(), spinners: ProgressSpinners::default(),
terminal_focused: true, terminal_focused: true,
prompt_active: false,
} }
} }
@ -133,7 +136,7 @@ impl EditorView {
if let Some(tabstops) = Self::tabstop_highlights(doc, theme) { if let Some(tabstops) = Self::tabstop_highlights(doc, theme) {
overlays.push(tabstops); overlays.push(tabstops);
} }
overlays.push(Self::doc_selection_highlights( overlays.push(self.doc_selection_highlights(
editor.mode(), editor.mode(),
doc, doc,
view, view,
@ -427,7 +430,8 @@ impl EditorView {
} }
/// Get highlight spans for selections in a document view. /// Get highlight spans for selections in a document view.
pub fn doc_selection_highlights( fn doc_selection_highlights(
&self,
mode: Mode, mode: Mode,
doc: &Document, doc: &Document,
view: &View, view: &View,
@ -481,9 +485,9 @@ impl EditorView {
// Special-case: cursor at end of the rope. // Special-case: cursor at end of the rope.
if range.head == range.anchor && range.head == text.len_chars() { if range.head == range.anchor && range.head == text.len_chars() {
if !selection_is_primary || !is_terminal_focused { if !selection_is_primary || !is_terminal_focused || self.prompt_active {
// Primary cursor is drawn by the terminal when focused // Primary cursor is drawn by the terminal when focused and no prompt is active
// Secondary cursors and unfocused primary cursor are drawn manually // Secondary cursors, unfocused primary cursor, and editor cursor when prompt is active are drawn manually
spans.push((cursor_scope, range.head..range.head + 1)); spans.push((cursor_scope, range.head..range.head + 1));
} }
continue; continue;
@ -502,16 +506,16 @@ impl EditorView {
}; };
spans.push((selection_scope, range.anchor..selection_end)); spans.push((selection_scope, range.anchor..selection_end));
// add cursors // add cursors
// skip primary cursor if terminal is focused - terminal cursor is used in that case // 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 { if !selection_is_primary || !is_terminal_focused || self.prompt_active {
spans.push((cursor_scope, cursor_start..range.head)); spans.push((cursor_scope, cursor_start..range.head));
} }
} else { } else {
// Reverse case. // Reverse case.
let cursor_end = next_grapheme_boundary(text, range.head); let cursor_end = next_grapheme_boundary(text, range.head);
// add cursors // add cursors
// skip primary cursor if terminal is focused - terminal cursor is used in that case // 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 { if !selection_is_primary || !is_terminal_focused || self.prompt_active {
spans.push((cursor_scope, range.head..cursor_end)); spans.push((cursor_scope, range.head..cursor_end));
} }
// non block cursors look like they exclude the cursor // non block cursors look like they exclude the cursor