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