1use cba::{bait::TransformExt, broc::EnvVars, env_vars, unwrap};
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, Debug)]
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; 2], 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: [false; 2],
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.cmp_replace(new_input.to_string());
115 if changed {
116 self.insert(Event::QueryChange);
117 }
118 changed
119 }
120
121 pub(crate) fn update_preview(&mut self, context: &str) -> bool {
122 let changed = self.preview_payload.cmp_replace(context.into());
123 if changed {
124 self.insert(Event::PreviewChange);
125 }
126 changed
127 }
128
129 pub(crate) fn update_preview_set(&mut self, context: String) -> bool {
130 let next = Some(context);
131 let changed = self.preview_set_payload.cmp_replace(next);
132 if changed {
133 self.insert(Event::PreviewSet);
134 }
135 changed
136 }
137
138 pub(crate) fn update_preview_unset(&mut self) {
139 let changed = self.preview_set_payload.cmp_replace(None);
140 if changed {
141 self.insert(Event::PreviewSet);
142 }
143 }
144
145 pub(crate) fn update_layout(&mut self, new_layout: [Rect; 4]) -> bool {
146 let changed = self.layout.cmp_replace(new_layout);
147 if changed {
148 self.insert(Event::Resize);
149 }
150 changed
151 }
152
153 pub(crate) fn update_preview_visible(&mut self, preview_ui: &PreviewUI) -> bool {
155 let visible = preview_ui.visible();
156 let changed = self.preview_visible.cmp_replace(visible);
157 if changed && visible {
158 self.insert(Event::PreviewChange);
159 }
160 changed
161 }
162
163 pub(crate) fn update<'a, T: SSS, S: Selection, A: ActionExt>(
164 &'a mut self,
165 picker_ui: &'a PickerUI<T, S>,
166 overlay_ui: &'a Option<OverlayUI<A>>,
167 ) {
168 if self.iterations == 0 {
169 self.insert(Event::Start);
170 }
171 self.iterations += 1;
172
173 self.update_input(&picker_ui.input.input);
174 self.col = picker_ui.results.col();
175
176 let status = &picker_ui.results.status;
177 self.synced[1] |= status.running;
178 if status.changed {
179 if !picker_ui.results.status.running {
181 if !self.synced[0] {
182 if picker_ui.results.status.item_count > 0 {
184 self.insert(Event::Synced);
185 self.synced[0] = true;
186 }
187 } else {
188 log::trace!("resynced on iteration {}", self.iterations);
191 self.insert(Event::Resynced);
192 }
193 }
194 }
195
196 if let Some(o) = overlay_ui {
197 if self.overlay_index != o.index() {
198 self.insert(Event::OverlayChange);
199 self.overlay_index = o.index()
200 }
201 self.overlay_index = o.index()
202 }
203
204 let new_id = get_current(picker_ui).map(|x| x.0);
205 let changed = self.last_id != get_current(picker_ui).map(|x| x.0);
206 if changed {
207 self.last_id = new_id;
208 self.insert(Event::CursorChange);
209 }
210 }
212
213 pub fn dispatcher<'a, 'b: 'a, T: SSS, S: Selection>(
216 &'a mut self,
217 ui: &'a mut UI,
218 picker_ui: &'a mut PickerUI<'b, T, S>,
219 footer_ui: &'a mut DisplayUI,
220 preview_ui: &'a mut Option<PreviewUI>,
221 event_controller: &'a EventSender,
222 ) -> MMState<'a, 'b, T, S> {
223 MMState {
224 state: self,
225 ui,
226 picker_ui,
227 footer_ui,
228 preview_ui,
229 event_controller,
230 }
231 }
232
233 fn reset(&mut self) {
234 }
236
237 pub(crate) fn events(&mut self) -> Event {
238 self.reset();
239 std::mem::take(&mut self.events)
240 }
241}
242
243pub struct MMState<'a, 'b: 'a, T: SSS, S: Selection> {
245 pub(crate) state: &'a mut State,
247
248 pub ui: &'a mut UI,
249 pub picker_ui: &'a mut PickerUI<'b, T, S>,
250 pub footer_ui: &'a mut DisplayUI,
251 pub preview_ui: &'a mut Option<PreviewUI>,
252 pub event_controller: &'a EventSender,
253}
254
255impl<'a, 'b: 'a, T: SSS, S: Selection> MMState<'a, 'b, T, S> {
256 pub fn previewer_area(&self) -> Option<&Rect> {
257 self.preview_ui.as_ref().map(|ui| &ui.area)
258 }
259
260 pub fn ui_area(&self) -> &Rect {
261 &self.ui.area
262 }
263 pub fn ui_size(&self) -> [u16; 2] {
264 let q = &self.ui.area;
265 [
266 q.width.saturating_sub(self.ui.config.border.width()),
267 q.height.saturating_sub(self.ui.config.border.width()),
268 ]
269 }
270
271 pub fn current_item(&self) -> Option<S> {
272 get_current(self.picker_ui).map(|s| s.1)
273 }
274
275 pub fn current_raw(&self) -> Option<&T> {
277 self.picker_ui
278 .worker
279 .get_nth(self.picker_ui.results.index())
280 }
281 pub fn map_selected_to_vec<U>(&self, mut f: impl FnMut(&S) -> U) -> Vec<U> {
283 if !self.picker_ui.selector.is_empty() {
284 self.picker_ui.selector.map_to_vec(f)
285 } else {
286 get_current(self.picker_ui)
287 .iter()
288 .map(|s| f(&s.1))
289 .collect()
290 }
291 }
292
293 pub fn injector(&self) -> WorkerInjector<T> {
294 self.picker_ui.worker.injector()
295 }
296
297 pub fn widths(&self) -> &Vec<u16> {
298 self.picker_ui.results.widths()
299 }
300
301 pub fn status(&self) -> &Status {
302 &self.picker_ui.results.status
304 }
305
306 pub fn selections(&self) -> &Selector<T, S> {
307 &self.picker_ui.selector
308 }
309
310 pub fn preview_visible(&self) -> bool {
311 self.preview_ui.as_ref().is_some_and(|s| s.visible())
312 }
313
314 pub fn get_content_and_index(&self) -> (String, u32) {
315 (
316 self.picker_ui.input.input.clone(),
317 self.picker_ui.results.index(),
318 )
319 }
320
321 pub fn restart_worker(&mut self) {
322 self.picker_ui.worker.restart(false);
323 self.state.synced = [false; 2];
324 }
325
326 pub fn make_env_vars(&self) -> EnvVars {
327 env_vars! {
328 "FZF_LINES" => self.ui_area().height.to_string(),
329 "FZF_COLUMNS" => self.ui_area().width.to_string(),
330 "FZF_TOTAL_COUNT" => self.status().item_count.to_string(),
331 "FZF_MATCH_COUNT" => self.status().matched_count.to_string(),
332 "FZF_SELECT_COUNT" => self.selections().len().to_string(),
333 "FZF_POS" => get_current(self.picker_ui).map_or("".to_string(), |x| format!("{}", x.0)),
334 "FZF_QUERY" => self.input.clone(),
335 }
336 }
337
338 pub fn stash_preview_visibility(&mut self, show: Option<bool>) {
343 log::trace!("Called stash_preview_visibility with {show:?}");
344 let p = unwrap!(self.preview_ui);
345 if let Some(s) = show {
346 self.state.stashed_preview_visibility = Some(p.visible());
347 p.show(s);
348 } else if let Some(s) = self.state.stashed_preview_visibility.take() {
349 p.show(s);
350 }
351 }
352}
353
354pub(crate) fn get_current<T: SSS, S: Selection>(picker_ui: &PickerUI<T, S>) -> Option<(u32, S)> {
355 let current_raw = picker_ui.worker.get_nth(picker_ui.results.index());
356 current_raw.map(picker_ui.selector.identifier)
357}
358
359impl<'a, 'b: 'a, T: SSS, S: Selection> std::ops::Deref for MMState<'a, 'b, T, S> {
361 type Target = State;
362
363 fn deref(&self) -> &Self::Target {
364 self.state
365 }
366}
367
368impl<'a, 'b: 'a, T: SSS, S: Selection> std::ops::DerefMut for MMState<'a, 'b, T, S> {
369 fn deref_mut(&mut self) -> &mut Self::Target {
370 self.state
371 }
372}