1use cli_boilerplate_automation::{broc::EnvVars, env_vars};
2use std::{collections::HashSet, ops::Deref};
3
4use crate::{
5 SSS, Selection, Selector,
6 action::ActionExt,
7 message::Event,
8 nucleo::{Status, injector::WorkerInjector},
9 ui::{OverlayUI, PickerUI, PreviewUI, Rect, UI},
10};
11
12#[derive(Default)]
16pub struct State<S: Selection> {
17 pub current: Option<(u32, S)>,
18 pub input: String,
19 pub col: Option<usize>,
20
21 pub(crate) preview_run: String,
22 pub(crate) preview_set: Option<String>,
23 pub iterations: u32,
24 pub preview_show: bool,
25 pub layout: [Rect; 4], pub overlay_index: Option<usize>,
27
28 pub(crate) matcher_running: bool,
29 pub(crate) events: HashSet<Event>,
30}
31
32pub struct MMState<'a, T: SSS, S: Selection> {
33 pub(crate) state: &'a State<S>,
34
35 pub picker_ui: &'a PickerUI<'a, T, S>,
36 pub ui: &'a UI,
37 pub preview_ui: Option<&'a PreviewUI>,
38}
39
40impl<'a, T: SSS, S: Selection> MMState<'a, T, S> {
41 pub fn previewer_area(&self) -> Option<&Rect> {
42 self.preview_ui.map(|ui| &ui.area)
43 }
44
45 pub fn ui_area(&self) -> &Rect {
46 &self.ui.area
47 }
48
49 pub fn current_raw(&self) -> Option<&T> {
50 self.picker_ui
51 .worker
52 .get_nth(self.picker_ui.results.index())
53 }
54 pub fn map_selected_to_vec<U>(&self, mut f: impl FnMut(&S) -> U) -> Vec<U> {
56 if !self.picker_ui.selections.is_empty() {
57 self.picker_ui.selections.map_to_vec(f)
58 } else {
59 self.current.iter().map(|s| f(&s.1)).collect()
60 }
61 }
62
63 pub fn injector(&self) -> WorkerInjector<T> {
64 self.picker_ui.worker.injector()
65 }
66
67 pub fn widths(&self) -> &Vec<u16> {
68 self.picker_ui.results.widths()
69 }
70
71 pub fn status(&self) -> &Status {
72 &self.picker_ui.results.status
74 }
75
76 pub fn selections(&self) -> &Selector<T, S> {
77 &self.picker_ui.selections
78 }
79
80 pub fn make_env_vars(&self) -> EnvVars {
81 env_vars! {
82 "FZF_LINES" => self.ui_area().height.to_string(),
83 "FZF_COLUMNS" => self.ui_area().width.to_string(),
84 "FZF_TOTAL_COUNT" => self.status().item_count.to_string(),
85 "FZF_MATCH_COUNT" => self.status().matched_count.to_string(),
86 "FZF_SELECT_COUNT" => self.selections().len().to_string(),
87 "FZF_POS" => self.current.as_ref().map_or("".to_string(), |x| format!("{}", x.0)),
88 "FZF_QUERY" => self.input.clone(),
89 }
90 }
91
92 }
96
97impl<S: Selection> State<S> {
98 pub fn new() -> Self {
99 Self {
101 current: None,
102
103 preview_run: String::new(),
104 preview_set: None,
105 preview_show: false,
106 layout: [Rect::default(); 4],
107 overlay_index: None,
108 col: None,
109
110 input: String::new(),
111 iterations: 0,
112 matcher_running: true,
113
114 events: HashSet::new(),
115 }
116 }
117 pub(crate) fn take_current(&mut self) -> Option<S> {
119 self.current.take().map(|x| x.1)
120 }
121
122 pub fn preview_payload(&self) -> &String {
123 &self.preview_run
124 }
125
126 pub fn contains(&self, event: &Event) -> bool {
127 self.events.contains(event)
128 }
129
130 pub fn insert(&mut self, event: Event) -> bool {
131 self.events.insert(event)
132 }
133
134 pub fn preview_set_payload(&self) -> &Option<String> {
135 &self.preview_set
136 }
137
138 pub(crate) fn update_current<T: SSS>(
140 &mut self,
141 picker_ui: &PickerUI<T, S>,
142 ) {
144 let current_raw = picker_ui.worker.get_nth(picker_ui.results.index());
145 let new_current = current_raw.map(picker_ui.selections.identifier);
146
147 let changed = self.current.as_ref().map(|x| x.0) != new_current.as_ref().map(|x| x.0);
148 if changed {
149 self.current = new_current;
150 self.insert(Event::CursorChange);
151 }
152 }
153
154 pub(crate) fn update_input(&mut self, new_input: &str) -> bool {
155 let changed = self.input != new_input;
156 if changed {
157 self.input = new_input.to_string();
158 self.insert(Event::QueryChange);
159 }
160 changed
161 }
162
163 pub(crate) fn update_preview(&mut self, context: &str) -> bool {
164 let changed = self.preview_run != context;
165 if changed {
166 self.preview_run = context.into();
167 self.insert(Event::PreviewChange);
168 }
169 changed
170 }
171
172 pub(crate) fn update_preview_set(&mut self, context: String) -> bool {
173 let next = Some(context);
174 let changed = self.preview_set != next;
175 if changed {
176 self.preview_set = next;
177 self.insert(Event::PreviewSet);
178 }
179 changed
180 }
181
182 pub(crate) fn update_preview_unset(&mut self) {
183 self.preview_set = None;
184 self.insert(Event::PreviewSet);
185 }
186
187 pub(crate) fn update_layout(&mut self, new_layout: [Rect; 4]) -> bool {
188 let changed = self.layout != new_layout;
189 if changed {
190 self.insert(Event::Resize);
191 self.layout = new_layout;
192 }
193 changed
194 }
195
196 pub(crate) fn update_preview_ui(&mut self, preview_ui: &PreviewUI) -> bool {
198 let next = preview_ui.is_show();
199 let changed = self.preview_show != next;
200 self.preview_show = next;
201 if changed && next {
203 self.insert(Event::PreviewChange);
204 };
205 changed
206 }
207
208 pub(crate) fn update<'a, T: SSS, A: ActionExt>(
209 &'a mut self,
210 picker_ui: &'a PickerUI<T, S>,
211 overlay_ui: &'a Option<OverlayUI<A>>,
212 ) {
213 if self.iterations == 0 {
214 self.insert(Event::Start);
215 }
216 self.iterations += 1;
217
218 self.update_input(&picker_ui.input.input);
219 self.col = picker_ui.results.col();
220
221 if self.matcher_running != picker_ui.results.status.running {
222 if !picker_ui.results.status.running {
223 self.insert(Event::Synced);
224 }
225 self.matcher_running = picker_ui.results.status.running;
226 };
227
228 if let Some(o) = overlay_ui {
229 if self.overlay_index != o.index() {
230 self.insert(Event::OverlayChange);
231 self.overlay_index = o.index()
232 }
233 self.overlay_index = o.index()
234 }
235 self.update_current(picker_ui);
236 }
237
238 pub(crate) fn dispatcher<'a, T: SSS>(
240 &'a self,
241 ui: &'a UI,
242 picker_ui: &'a PickerUI<T, S>,
243 preview_ui: Option<&'a PreviewUI>,
244 ) -> MMState<'a, T, S> {
245 MMState {
246 state: self,
247 picker_ui,
248 ui,
249 preview_ui,
250 }
251 }
252
253 fn reset(&mut self) {
254 }
256
257 pub(crate) fn events(&mut self) -> HashSet<Event> {
258 self.reset();
259 std::mem::take(&mut self.events) }
262}
263
264impl<S: Selection> std::fmt::Debug for State<S> {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 f.debug_struct("State")
268 .field("input", &self.input)
269 .field("preview_payload", &self.preview_run)
270 .field("iterations", &self.iterations)
271 .field("preview_show", &self.preview_show)
272 .field("layout", &self.layout)
273 .field("events", &self.events)
274 .finish_non_exhaustive()
275 }
276}
277
278impl<'a, T: SSS, S: Selection> Deref for MMState<'a, T, S> {
279 type Target = State<S>;
280
281 fn deref(&self) -> &Self::Target {
282 self.state
283 }
284}