1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! Cursor appearance — the shared text-field cursor helper used by the
//! editor views and modal inputs alike.
//!
//! Every cursor in edamame is *fake*: a styled buffer cell, never the
//! terminal's hardware cursor. The hardware cursor can show only one
//! position at a time and its color can't be set portably (OSC 12 is
//! unsupported by kitty and has broken restore in VTE), so a fake cursor is
//! what lets us (a) color the cursor per context and (b) show the editor
//! cursor and a modal input cursor at the same time.
//!
//! The cursor is always a **block**: the cell at the insertion point is
//! recolored with the cursor style while the underlying character stays
//! visible. Shape is uniform across the app, so context is signalled by
//! *color* — the editor cursor mirrors the per-mode status chip
//! (`app::cursor_style`), and modal inputs use `cursor` (see
//! `docs/dev/theming.md`).
//!
//! # Two block-cursor mechanisms
//!
//! There are two ways to render that block, and they are NOT interchangeable
//! — the choice is forced by whether the call site controls the styling of the
//! cursor's individual cell:
//!
//! 1. **Recolor-the-cell** ([`text_field_spans`]) — the preferred form. The
//! character under the cursor keeps its glyph and is re-*styled* with the
//! cursor color; past end-of-line a single styled space stands in. The
//! output is always exactly one cell wide and the glyph stays legible, so
//! the field neither jitters on blink nor hides the character it sits on.
//! Use this anywhere the value is assembled here as its own `Span`s
//! (modal text inputs, the vim command line).
//!
//! 2. **Insert-a-glyph** ([`CURSOR_BLOCK`], the U+2588 full block) — the
//! fallback. Some rows (`settings_overlay`, `export_theme_modal`) push the
//! value through a shared row formatter (`format_modal_row`) or a
//! horizontal-scroll window that owns the cell styling, so we can't hand
//! those layers a pre-styled cursor cell. Instead we splice a literal `█`
//! glyph into the string. Unlike mechanism 1 this is a *true glyph*: it
//! occupies its own cell and obscures nothing (it is only ever placed at an
//! append-only end-of-value position, so there is no character to hide).
//! To stay blink-stable the caller MUST emit a same-width space on the
//! hidden phase — `█` when visible, `" "` when not — or the field width
//! changes between blink phases.
//!
//! Reach for mechanism 2 only when mechanism 1 genuinely can't apply; don't
//! "unify" the `█` sites onto the recolor path without first giving those rows
//! per-cell styling control.
use Style;
use Span;
/// Full-cell block glyph (U+2588) used as the cursor where a value flows
/// through a uniform row formatter (`settings_overlay`, `export_theme_modal`)
/// and a separately-styled cursor cell isn't available — the glyph itself
/// reads as the block. Prefer [`text_field_spans`] wherever the cursor cell
/// can carry its own style.
pub const CURSOR_BLOCK: char = '█';
/// Build the three styled spans for a single-line text field's value with a
/// blink-stable block cursor at char index `cursor`.
///
/// The middle span is always exactly one cell wide — the character under the
/// cursor (or a space when the cursor sits past the last char) styled
/// `cursor_style` when `visible`, and styled `value_style` (i.e. shown as
/// ordinary text) when not — so the field's rendered width never changes
/// between blink phases. Callers push the three spans in order, surrounded by
/// their own label / padding spans.
/// Split `s` at char index `cursor` into two owned halves. `cursor` past
/// the end yields `(s.to_owned(), String::new())`.