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