1use bitflags::bitflags;
2use crossterm::event::{self};
3use log::{debug, error, warn};
4use ratatui::{
5 layout::{Constraint, Direction, Layout, Position, Rect},
6 style::{Style, Stylize},
7 text::Text,
8 widgets::{Block, Borders, Paragraph, Row, Table, Wrap},
9};
10use rustc_hash::FxHashSet;
11use std::{
12 cell::{RefCell, RefMut},
13 collections::HashSet,
14 fmt::{Formatter, Write},
15 ops::Deref,
16 sync::Arc,
17};
18use unicode_segmentation::UnicodeSegmentation;
19use unicode_width::UnicodeWidthStr;
20
21use crate::{
22 PickerItem, Selection, SelectionSet,
23 action::Action,
24 config::{
25 InputConfig, PreviewConfig, PreviewSetting, RenderConfig, ResultsConfig,
26 TerminalLayoutSettings,
27 },
28 env_vars,
29 message::Event,
30 nucleo::{injector::WorkerInjector, worker::{Status, Worker}},
31 spawn::{EnvVars, preview::PreviewerView},
32 tui::Tui,
33 ui::{PickerUI, PreviewUI, UI},
34 utils::text::{
35 clip_text_lines, fit_width, grapheme_index_to_byte_index, prefix_text, substitute_escaped,
36 },
37};
38
39pub struct State<S: Selection, C> {
43 pub current: Option<(u32, S)>,
44 pub input: String,
45 pub col: Option<usize>,
46
47 preview_payload: String,
48 pub context: Arc<C>,
51 pub iterations: u32,
52 pub preview_show: bool,
53 pub layout: [Rect; 4],
54
55 events: HashSet<Event>,
56}
57
58pub struct EphemeralState<'a, T: PickerItem, S: Selection, C> {
59 state: &'a State<S, C>,
60
61 pub picker_ui: &'a PickerUI<'a, T, S, C>,
62 pub area: &'a Rect,
63 pub previewer_area: Option<Rect>,
64 pub effects: Effects,
65}
66
67impl<'a, T: PickerItem, S: Selection, C> EphemeralState<'a, T, S, C> {
68 pub fn new(
69 state: &'a State<S, C>,
70 picker_ui: &'a PickerUI<T, S, C>,
71 area: &'a Rect,
72 previewer_area: Option<Rect>,
73 ) -> Self {
74 Self {
75 state,
76 picker_ui,
77 area,
78 previewer_area,
79 effects: Effects::empty(),
80 }
81 }
82
83 pub fn current_raw(&self) -> Option<&T> {
84 self.picker_ui.worker.get_nth(self.picker_ui.results.index())
85 }
86
87 pub fn injector(&self) -> WorkerInjector<T, C> {
88 self.picker_ui.worker.injector()
89 }
90
91 pub fn widths(&self) -> &Vec<u16> {
92 self.picker_ui.results.widths()
93 }
94
95 pub fn status(&self) -> &Status { &self.picker_ui.results.status
97 }
98
99 pub fn selections(&self) -> &SelectionSet<T, S> {
100 &self.picker_ui.selections
101 }
102 pub fn make_env_vars(&self) -> EnvVars {
103 env_vars! {
104 "FZF_LINES" => self.area.height.to_string(),
105 "FZF_COLUMNS" => self.area.width.to_string(),
106 "FZF_TOTAL_COUNT" => self.status().item_count.to_string(),
107 "FZF_MATCH_COUNT" => self.status().matched_count.to_string(),
108 "FZF_SELECT_COUNT" => self.selections().len().to_string(),
109 "FZF_POS" => self.current.as_ref().map_or("".to_string(), |x| format!("{}", x.0)),
110 "FZF_QUERY" => self.input.clone(),
111 }
112 }
113}
114
115impl<S: Selection, C> State<S, C> {
116 pub fn new(context: Arc<C>) -> Self {
117 Self {
118 current: None,
119
120 preview_payload: String::new(),
121 preview_show: false,
122 layout: [Rect::default(); 4],
123 col: None,
124
125 context,
126 input: String::new(),
127 iterations: 0,
128
129 events: HashSet::new(),
130 }
131 }
132
133 pub fn current(&mut self) -> Option<S> {
134 self.current.take().map(|x| x.1)
135 }
136
137 pub fn preview_payload(&self) -> &String {
138 &self.preview_payload
139 }
140
141 pub fn contains(&self, event: &Event) -> bool {
142 self.events.contains(event)
143 }
144
145 pub fn insert(&mut self, event: Event) -> bool {
146 self.events.insert(event)
147 }
148
149 fn reset(&mut self) {
150 self.iterations += 1;
151 }
152
153 pub fn update_current(&mut self, new_current: Option<(u32, S)>) -> bool {
154 let changed = self.current != new_current;
155 if changed {
156 self.current = new_current;
157 self.insert(Event::CursorChange);
158 }
159 changed
160 }
161
162 pub fn update_input(&mut self, new_input: &str) -> bool {
163 let changed = self.input != new_input;
164 if changed {
165 self.input = new_input.to_string();
166 self.insert(Event::QueryChange);
167 }
168 changed
169 }
170
171 pub fn update_preview(&mut self, context: &str) -> bool {
172 let next = context;
173 let changed = self.preview_payload != next;
174 if changed {
175 self.preview_payload = next.into();
176 self.insert(Event::PreviewChange);
177 }
178 changed
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
202
203 pub fn update<'a, T: PickerItem>(&'a mut self, picker_ui: &'a PickerUI<T, S, C>){
204 self.update_input(&picker_ui.input.input);
205 self.col = picker_ui.results.col();
206
207 let current_raw = picker_ui.worker.get_nth(picker_ui.results.index());
208 self.update_current(current_raw.map(picker_ui.selections.identifier));
209 }
210
211 pub fn dispatcher<'a, T: PickerItem>(&'a self, ui: &'a UI, picker_ui: &'a PickerUI<T, S, C>, preview_ui: Option<&PreviewUI>) -> EphemeralState<'a, T, S, C> {
212 EphemeralState::new(&self,
213 picker_ui,
214 &ui.area,
215 preview_ui.map(|p| p.area),
216 )
217 }
218
219 pub fn events(
220 &mut self,
221 ) -> HashSet<Event> {
222 let mut ret = std::mem::take(&mut self.events);
223 self.reset();
224 ret
225 }
226}
227
228bitflags! {
229 #[derive(Clone, Copy)]
230 pub struct Effects: u32 {
231 }
233}
234
235
236
237impl<S: Selection, C> std::fmt::Debug for State<S, C> {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 f.debug_struct("State")
241 .field("input", &self.input)
242 .field("preview_payload", &self.preview_payload)
243 .field("iterations", &self.iterations)
244 .field("preview_show", &self.preview_show)
245 .field("layout", &self.layout)
246 .field("events", &self.events)
247 .finish_non_exhaustive()
248 }
249}
250
251impl<'a, T: PickerItem, S: Selection, C> Deref for EphemeralState<'a, T, S, C> {
252 type Target = State<S, C>;
253
254 fn deref(&self) -> &Self::Target {
255 self.state
256 }
257}
258
259impl<'a, T: PickerItem, S: Selection, L> Clone for EphemeralState<'a, T, S, L> {
260 fn clone(&self) -> Self {
261 Self {
262 state: self.state,
263 area: self.area,
264 picker_ui: self.picker_ui,
265 previewer_area: self.previewer_area,
266 effects: self.effects,
267 }
268 }
269}