1use cli_boilerplate_automation::{broc::EnvVars, env_vars};
2
3use crate::{
4 SSS, Selection, Selector,
5 action::ActionExt,
6 event::EventSender,
7 message::{Event, Interrupt},
8 nucleo::{Status, injector::WorkerInjector},
9 ui::{DisplayUI, OverlayUI, PickerUI, PreviewUI, Rect, UI},
10};
11
12#[derive(Default)]
14pub struct State {
15 last_id: Option<u32>,
16 interrupt: Interrupt,
17 interrupt_payload: String,
18
19 pub(crate) input: String,
21 pub(crate) col: Option<usize>,
22 pub(crate) iterations: u32,
23 pub(crate) preview_visible: bool,
24 pub(crate) layout: [Rect; 4], pub(crate) overlay_index: Option<usize>,
26 pub(crate) synced: bool,
27
28 pub(crate) events: Event,
29
30 pub preview_set_payload: Option<String>,
32 pub preview_payload: String,
34 stashed_preview_visibility: Option<bool>,
36 pub should_quit: bool,
40 pub should_quit_nomatch: bool,
42 pub filtering: bool,
43}
44
45impl State {
46 pub fn new() -> Self {
47 Self {
49 last_id: None,
50 interrupt: Interrupt::None,
51 interrupt_payload: String::new(),
52
53 preview_payload: String::new(),
54 preview_set_payload: None,
55 preview_visible: false,
56 stashed_preview_visibility: None,
57 layout: [Rect::default(); 4],
58 overlay_index: None,
59 col: None,
60
61 input: String::new(),
62 iterations: 0,
63 synced: true,
64
65 events: Event::empty(),
66 should_quit: false,
67 should_quit_nomatch: false,
68 filtering: true,
69 }
70 }
71 pub fn contains(&self, event: Event) -> bool {
74 self.events.contains(event)
75 }
76
77 pub fn payload(&self) -> &String {
78 &self.interrupt_payload
79 }
80
81 pub fn interrupt(&self) -> Interrupt {
82 self.interrupt
83 }
84
85 pub fn set_interrupt(&mut self, interrupt: Interrupt, payload: String) {
86 self.interrupt = interrupt;
87 self.interrupt_payload = payload;
88 }
89
90 pub fn clear_interrupt(&mut self) {
91 self.interrupt = Interrupt::None;
92 self.interrupt_payload.clear();
93 }
94
95 pub fn insert(&mut self, event: Event) {
96 self.events.insert(event);
97 }
98
99 pub fn overlay_index(&self) -> Option<usize> {
100 self.overlay_index
101 }
102 pub fn preview_set_payload(&self) -> Option<String> {
103 self.preview_set_payload.clone()
104 }
105 pub fn preview_payload(&self) -> &String {
106 &self.preview_payload
107 }
108 pub fn stashed_preview_visibility(&self) -> Option<bool> {
109 self.stashed_preview_visibility
110 }
111
112 pub(crate) fn update_input(&mut self, new_input: &str) -> bool {
114 let changed = self.input != new_input;
115 if changed {
116 self.input = new_input.to_string();
117 self.insert(Event::QueryChange);
118 }
119 changed
120 }
121
122 pub(crate) fn update_preview(&mut self, context: &str) -> bool {
123 let changed = self.preview_payload != context;
124 if changed {
125 self.preview_payload = context.into();
126 self.insert(Event::PreviewChange);
127 }
128 changed
129 }
130
131 pub(crate) fn update_preview_set(&mut self, context: String) -> bool {
132 let next = Some(context);
133 let changed = self.preview_set_payload != next;
134 if changed {
135 self.preview_set_payload = next;
136 self.insert(Event::PreviewSet);
137 }
138 changed
139 }
140
141 pub(crate) fn update_preview_unset(&mut self) {
142 self.preview_set_payload = None;
143 self.insert(Event::PreviewSet);
144 }
145
146 pub(crate) fn update_layout(&mut self, new_layout: [Rect; 4]) -> bool {
147 let changed = self.layout != new_layout;
148 if changed {
149 self.insert(Event::Resize);
150 self.layout = new_layout;
151 }
152 changed
153 }
154
155 pub(crate) fn update_preview_ui(&mut self, preview_ui: &PreviewUI) -> bool {
157 let changed = self.preview_visible != preview_ui.is_show();
158 if changed && preview_ui.is_show() {
160 self.insert(Event::PreviewChange);
161 self.preview_visible = true;
162 };
163 changed
164 }
165
166 pub(crate) fn update<'a, T: SSS, S: Selection, A: ActionExt>(
167 &'a mut self,
168 picker_ui: &'a PickerUI<T, S>,
169 overlay_ui: &'a Option<OverlayUI<A>>,
170 ) {
171 if self.iterations == 0 {
172 self.insert(Event::Start);
173 }
174 self.iterations += 1;
175
176 self.update_input(&picker_ui.input.input);
177 self.col = picker_ui.results.col();
178
179 let status = &picker_ui.results.status;
180 if status.changed {
181 if !picker_ui.results.status.running {
183 if !self.synced {
184 self.insert(Event::Synced);
185 self.synced = true;
186 } else {
187 self.insert(Event::Resynced);
188 }
189 }
190 }
191
192 if let Some(o) = overlay_ui {
193 if self.overlay_index != o.index() {
194 self.insert(Event::OverlayChange);
195 self.overlay_index = o.index()
196 }
197 self.overlay_index = o.index()
198 }
199
200 let new_id = get_current(picker_ui).map(|x| x.0);
201 let changed = self.last_id != get_current(picker_ui).map(|x| x.0);
202 if changed {
203 self.last_id = new_id;
204 self.insert(Event::CursorChange);
205 }
206 }
207
208 pub(crate) fn dispatcher<'a, 'b: 'a, T: SSS, S: Selection>(
210 &'a mut self,
211 ui: &'a mut UI,
212 picker_ui: &'a mut PickerUI<'b, T, S>,
213 footer_ui: &'a mut DisplayUI,
214 preview_ui: &'a mut Option<PreviewUI>,
215 event_controller: &'a EventSender,
216 ) -> MMState<'a, 'b, T, S> {
217 MMState {
218 state: self,
219 ui,
220 picker_ui,
221 footer_ui,
222 preview_ui,
223 event_controller,
224 }
225 }
226
227 fn reset(&mut self) {
228 }
230
231 pub(crate) fn events(&mut self) -> Event {
232 self.reset();
233 std::mem::take(&mut self.events)
234 }
235}
236
237pub struct MMState<'a, 'b: 'a, T: SSS, S: Selection> {
239 pub(crate) state: &'a mut State,
241
242 pub ui: &'a mut UI,
243 pub picker_ui: &'a mut PickerUI<'b, T, S>,
244 pub footer_ui: &'a mut DisplayUI,
245 pub preview_ui: &'a mut Option<PreviewUI>,
246 pub event_controller: &'a EventSender,
247}
248
249impl<'a, 'b: 'a, T: SSS, S: Selection> MMState<'a, 'b, T, S> {
250 pub fn previewer_area(&self) -> Option<&Rect> {
251 self.preview_ui.as_ref().map(|ui| &ui.area)
252 }
253
254 pub fn ui_area(&self) -> &Rect {
255 &self.ui.area
256 }
257
258 pub fn current_item(&self) -> Option<S> {
259 get_current(self.picker_ui).map(|s| s.1)
260 }
261
262 pub fn current_raw(&self) -> Option<&T> {
264 self.picker_ui
265 .worker
266 .get_nth(self.picker_ui.results.index())
267 }
268 pub fn map_selected_to_vec<U>(&self, mut f: impl FnMut(&S) -> U) -> Vec<U> {
270 if !self.picker_ui.selector.is_empty() {
271 self.picker_ui.selector.map_to_vec(f)
272 } else {
273 get_current(self.picker_ui)
274 .iter()
275 .map(|s| f(&s.1))
276 .collect()
277 }
278 }
279
280 pub fn injector(&self) -> WorkerInjector<T> {
281 self.picker_ui.worker.injector()
282 }
283
284 pub fn widths(&self) -> &Vec<u16> {
285 self.picker_ui.results.widths()
286 }
287
288 pub fn status(&self) -> &Status {
289 &self.picker_ui.results.status
291 }
292
293 pub fn selections(&self) -> &Selector<T, S> {
294 &self.picker_ui.selector
295 }
296
297 pub fn preview_visible(&self) -> bool {
298 self.preview_ui.as_ref().is_some_and(|s| s.is_show())
299 }
300
301 pub fn get_content_and_index(&self) -> (String, u32) {
302 (
303 self.picker_ui.input.input.clone(),
304 self.picker_ui.results.index(),
305 )
306 }
307
308 pub fn make_env_vars(&self) -> EnvVars {
309 env_vars! {
310 "FZF_LINES" => self.ui_area().height.to_string(),
311 "FZF_COLUMNS" => self.ui_area().width.to_string(),
312 "FZF_TOTAL_COUNT" => self.status().item_count.to_string(),
313 "FZF_MATCH_COUNT" => self.status().matched_count.to_string(),
314 "FZF_SELECT_COUNT" => self.selections().len().to_string(),
315 "FZF_POS" => get_current(self.picker_ui).map_or("".to_string(), |x| format!("{}", x.0)),
316 "FZF_QUERY" => self.input.clone(),
317 }
318 }
319
320 pub fn stash_preview_visibility(&mut self, show: Option<bool>) {
322 if let Some(p) = self.preview_ui {
323 if let Some(s) = show {
324 self.state.stashed_preview_visibility = Some(p.is_show());
325 p.show(s);
326 } else if let Some(s) = self.state.stashed_preview_visibility.take() {
327 p.show(s);
328 }
329 }
330 }
331}
332
333pub(crate) fn get_current<T: SSS, S: Selection>(picker_ui: &PickerUI<T, S>) -> Option<(u32, S)> {
334 let current_raw = picker_ui.worker.get_nth(picker_ui.results.index());
335 current_raw.map(picker_ui.selector.identifier)
336}
337
338impl std::fmt::Debug for State {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 f.debug_struct("State")
342 .field("input", &self.input)
343 .field("preview_payload", &self.preview_payload)
344 .field("iterations", &self.iterations)
345 .field("preview_show", &self.preview_visible)
346 .field("layout", &self.layout)
347 .field("events", &self.events)
348 .finish_non_exhaustive()
349 }
350}
351
352impl<'a, 'b: 'a, T: SSS, S: Selection> std::ops::Deref for MMState<'a, 'b, T, S> {
353 type Target = State;
354
355 fn deref(&self) -> &Self::Target {
356 self.state
357 }
358}
359
360impl<'a, 'b: 'a, T: SSS, S: Selection> std::ops::DerefMut for MMState<'a, 'b, T, S> {
361 fn deref_mut(&mut self) -> &mut Self::Target {
362 self.state
363 }
364}