lattice_cells/
headerline.rs1use std::sync::atomic::{AtomicU64, Ordering};
26use std::sync::{Arc, RwLock};
27
28use crate::cell::Cell;
29use crate::virtual_rows::{
30 AnchorPosition, ProviderId, VirtualRow, VirtualRowKind, VirtualRowProvider,
31};
32
33pub struct HeaderlineRow {
37 pub cells: Arc<[Cell]>,
40 pub bg: Option<u32>,
44}
45
46pub trait Headerline: Send + Sync + 'static {
54 fn version(&self) -> u64;
57
58 fn render(&self) -> Option<HeaderlineRow>;
62}
63
64pub struct SimpleHeaderline<S: Send + Sync + 'static> {
70 state: Arc<RwLock<S>>,
71 version: AtomicU64,
72 renderer: Arc<dyn Fn(&S) -> Option<HeaderlineRow> + Send + Sync>,
73}
74
75impl<S: Send + Sync + 'static> std::fmt::Debug for SimpleHeaderline<S> {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 f.debug_struct("SimpleHeaderline")
78 .field("version", &self.version.load(Ordering::Relaxed))
79 .finish_non_exhaustive()
80 }
81}
82
83impl<S: Send + Sync + 'static> Headerline for SimpleHeaderline<S> {
84 fn version(&self) -> u64 {
85 self.version.load(Ordering::Acquire)
86 }
87
88 fn render(&self) -> Option<HeaderlineRow> {
89 self.state.read().ok().and_then(|s| (self.renderer)(&s))
90 }
91}
92
93pub struct SimpleHeaderlineHandle<S: Send + Sync + 'static>(Arc<SimpleHeaderline<S>>);
103
104impl<S: Send + Sync + 'static> Clone for SimpleHeaderlineHandle<S> {
106 fn clone(&self) -> Self {
107 Self(Arc::clone(&self.0))
108 }
109}
110
111impl<S: Send + Sync + 'static> std::fmt::Debug for SimpleHeaderlineHandle<S> {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 write!(f, "SimpleHeaderlineHandle(v={})", self.version())
114 }
115}
116
117impl<S: Send + Sync + 'static> SimpleHeaderlineHandle<S> {
118 pub fn new(
123 initial: S,
124 renderer: impl Fn(&S) -> Option<HeaderlineRow> + Send + Sync + 'static,
125 ) -> Self {
126 Self(Arc::new(SimpleHeaderline {
127 state: Arc::new(RwLock::new(initial)),
128 version: AtomicU64::new(0),
129 renderer: Arc::new(renderer),
130 }))
131 }
132
133 pub fn update(&self, f: impl FnOnce(&mut S)) {
136 if let Ok(mut s) = self.0.state.write() {
137 f(&mut s);
138 }
139 self.0.version.fetch_add(1, Ordering::Release);
140 }
141
142 pub fn version(&self) -> u64 {
144 self.0.version.load(Ordering::Acquire)
145 }
146
147 pub fn provider(&self, provider_id: ProviderId) -> HeaderlineProvider {
150 HeaderlineProvider {
151 provider_id,
152 inner: Arc::clone(&self.0) as Arc<dyn Headerline>,
153 }
154 }
155}
156
157pub struct HeaderlineProvider {
165 provider_id: ProviderId,
166 inner: Arc<dyn Headerline>,
167}
168
169impl std::fmt::Debug for HeaderlineProvider {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 f.debug_struct("HeaderlineProvider")
172 .field("provider_id", &self.provider_id)
173 .field("version", &self.inner.version())
174 .finish()
175 }
176}
177
178impl HeaderlineProvider {
179 pub fn new(provider_id: ProviderId, inner: Arc<dyn Headerline>) -> Self {
182 Self { provider_id, inner }
183 }
184}
185
186impl VirtualRowProvider for HeaderlineProvider {
187 fn id(&self) -> ProviderId {
188 self.provider_id
189 }
190
191 fn version(&self) -> u64 {
192 self.inner.version()
193 }
194
195 fn collect(&self) -> Vec<VirtualRow> {
196 let Some(row) = self.inner.render() else {
197 return Vec::new();
198 };
199 vec![VirtualRow {
200 media: None,
201 anchor_line: 0,
202 position: AnchorPosition::Above,
203 cells: row.cells,
204 height: 1,
205 kind: VirtualRowKind::Sticky,
206 bg: row.bg,
207 scales: None,
208 gutter_line: None,
209 gutter_fg: None,
210 }]
211 }
212}
213
214#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn hidden_when_renderer_returns_none() {
222 let handle = SimpleHeaderlineHandle::new(0u32, |_| None);
223 let provider = handle.provider(1);
224 assert!(provider.collect().is_empty());
225 }
226
227 #[test]
228 fn emits_sticky_row_at_line_zero() {
229 let handle = SimpleHeaderlineHandle::<()>::new((), |_| {
230 let cells: Arc<[Cell]> = vec![Cell::new('x' as u32, 0xffffff, 0, 0)].into();
231 Some(HeaderlineRow {
232 cells,
233 bg: Some(0x000000),
234 })
235 });
236 let provider = handle.provider(42);
237 let rows = provider.collect();
238 assert_eq!(rows.len(), 1);
239 assert_eq!(rows[0].anchor_line, 0);
240 assert_eq!(rows[0].kind, VirtualRowKind::Sticky);
241 assert_eq!(rows[0].bg, Some(0x000000));
242 }
243
244 #[test]
245 fn version_advances_on_update() {
246 let handle = SimpleHeaderlineHandle::new(0u32, |_| None);
247 let v0 = handle.version();
248 handle.update(|s| *s = 1);
249 assert!(handle.version() > v0);
250 }
251
252 #[test]
253 fn provider_version_tracks_handle() {
254 let handle = SimpleHeaderlineHandle::new(0u32, |_| None);
255 let provider = handle.provider(99);
256 let v0 = provider.version();
257 handle.update(|s| *s += 1);
258 assert!(provider.version() > v0);
259 }
260
261 #[test]
262 fn direct_headerline_impl_works() {
263 struct Fixed;
264 impl Headerline for Fixed {
265 fn version(&self) -> u64 {
266 1
267 }
268 fn render(&self) -> Option<HeaderlineRow> {
269 let cells: Arc<[Cell]> = vec![Cell::new('!' as u32, 0, 0, 0)].into();
270 Some(HeaderlineRow { cells, bg: None })
271 }
272 }
273 let p = HeaderlineProvider::new(7, Arc::new(Fixed));
274 assert_eq!(p.collect().len(), 1);
275 assert_eq!(p.collect()[0].bg, None);
276 }
277}