Skip to main content

matchmaker/
action.rs

1use std::{
2    fmt::{self, Debug, Display},
3    str::FromStr,
4};
5
6use serde::{Deserialize, Serialize, Serializer};
7
8use crate::{MAX_ACTIONS, SSS, utils::serde::StringOrVec};
9
10/// Bindable actions
11/// # Additional
12/// See [crate::render::render_loop] for the source code definitions.
13#[derive(Debug, Clone, PartialEq)]
14pub enum Action<A: ActionExt = NullActionExt> {
15    /// Add item to selections
16    Select,
17    /// Remove item from selections
18    Deselect,
19    /// Toggle item in selections
20    Toggle,
21    /// Toggle all selections
22    CycleAll,
23    /// Clear all selections
24    ClearSelections,
25    /// Accept current selection
26    Accept,
27    /// Quit with code
28    Quit(i32),
29
30    // Results
31    /// Toggle wrap
32    ToggleWrap,
33
34    // Results Navigation
35    /// Move selection index up
36    Up(u16),
37    /// Move selection index down
38    Down(u16),
39    Pos(i32),
40    // TODO
41    PageDown,
42    // TODO
43    PageUp,
44    /// Horizontally scroll (the active column of) the current result.
45    /// 0 to reset.
46    HScroll(i8),
47    /// Vertically scroll the current result.
48    /// 0 to reset.
49    ///
50    /// (Rarely useful, unless you have an extremely long result whose wrap overflows.)
51    VScroll(i8),
52
53    // Preview
54    /// Cycle preview layouts
55    CyclePreview,
56    /// Show/hide preview for selection
57    Preview(String),
58    /// Show help in preview
59    Help(String),
60    /// Set preview layout;
61    /// None restores the command of the current layout.
62    SetPreview(Option<u8>),
63    /// Switch or toggle preview:
64    /// If an index is provided and the index is already current, the preview is hidden.
65    SwitchPreview(Option<u8>),
66    /// Toggle wrap in preview
67    TogglePreviewWrap,
68
69    // Preview navigation
70    /// Scroll preview up
71    PreviewUp(u16),
72    /// Scroll preview down
73    PreviewDown(u16),
74    /// Scroll preview half page up in rows.
75    /// If wrapping is enabled, the visual distance may exceed half a page.
76    PreviewHalfPageUp,
77    /// Scroll preview half page down in rows.
78    /// If wrapping is enabled, the visual distance may exceed half a page.
79    PreviewHalfPageDown,
80
81    // experimental
82    /// Persistent horizontal scroll
83    /// 0 to reset.
84    PreviewHScroll(i8),
85    /// Persistent single-line vertical scroll
86    /// 0 to reset.
87    PreviewScroll(i8),
88    /// Jump between start, end, last, and initial locations. (unimplemented).
89    PreviewJump,
90
91    /// Cycle columns
92    NextColumn,
93    /// Cycle columns backwards
94    PrevColumn,
95    /// Switch to a specific column
96    SwitchColumn(String),
97    /// Toggle visibility of a column
98    ToggleColumn(Option<String>),
99    /// Unhide a column, or all columns if None
100    ShowColumn(Option<String>),
101    ScrollLeft,
102    ScrollRight,
103
104    // Programmable
105    /// Execute command and continue
106    Execute(String),
107    /// Execute command without leaving the UI
108    ExecuteSilent(String),
109    /// Exit and become
110    Become(String),
111    /// Reload matcher/worker
112    Reload(String),
113    /// Print via handler
114    Print(String),
115    /// Print key via handler
116    PrintKey,
117    /// Store a value in the state
118    Store(String),
119
120    // Edit (Input)
121    /// Move cursor forward char
122    ForwardChar,
123    /// Move cursor backward char
124    BackwardChar,
125    /// Move cursor forward word
126    ForwardWord,
127    /// Move cursor backward word
128    BackwardWord,
129    /// Delete char
130    DeleteChar,
131    /// Delete word
132    DeleteWord,
133    /// Delete to start of line
134    DeleteLineStart,
135    /// Delete to end of line
136    DeleteLineEnd,
137    /// Clear input
138    Cancel,
139    /// Set input query
140    SetQuery(String),
141    /// Set query cursor pos
142    QueryPos(i32),
143
144    // Other/Experimental/Debugging
145    /// Insert char into input
146    Char(char),
147    /// Force redraw
148    Redraw,
149    /// Custom action
150    Custom(A),
151    /// Activate the nth overlay
152    Overlay(usize),
153    /// Alias for a semantic trigger
154    Semantic(String),
155}
156
157// --------------- MACROS ---------------
158
159/// # Example
160/// ```rust
161///     use matchmaker::{action::{Action, Actions, acs}, render::MMState};
162///     pub fn fsaction_aliaser(
163///         a: Action,
164///         state: &MMState<'_, '_, String, String>,
165///     ) -> Actions {
166///         match a {
167///             Action::Custom(_) => {
168///               log::debug!("Ignoring custom action");
169///               acs![]
170///             }
171///             _ => acs![a], // no change
172///         }
173///     }
174/// ```
175#[macro_export]
176macro_rules! acs {
177    ( $( $x:expr ),* $(,)? ) => {
178        {
179            $crate::action::Actions::from([$($x),*])
180        }
181    };
182}
183pub use crate::acs;
184
185/// # Example
186/// ```rust
187/// #[derive(Debug, Clone, PartialEq)]
188/// pub enum FsAction {
189///    Filters
190/// }
191///
192/// use matchmaker::{binds::{BindMap, bindmap, key}, action::Action};
193/// let default_config: BindMap<FsAction> = bindmap!(
194///    key!(alt-enter) => Action::Print("".into()),
195///    key!(alt-f), key!(ctrl-shift-f) => FsAction::Filters, // custom actions can be specified directly
196/// );
197/// ```
198#[macro_export]
199macro_rules! bindmap {
200    ( $( $( $k:expr ),+ => $v:expr ),* $(,)? ) => {{
201        let mut map = $crate::binds::BindMap::new();
202        $(
203            let action = $crate::action::Actions::from($v);
204            $(
205                map.insert($k.into(), action.clone());
206            )+
207        )*
208        map
209    }};
210} // btw, Can't figure out if its possible to support optional meta over inserts
211
212// --------------- ACTION_EXT ---------------
213
214pub trait ActionExt: Debug + Clone + PartialEq + SSS {}
215impl<T: Debug + Clone + PartialEq + SSS> ActionExt for T {}
216
217impl<T> From<T> for Action<T>
218where
219    T: ActionExt,
220{
221    fn from(value: T) -> Self {
222        Self::Custom(value)
223    }
224}
225#[derive(Debug, Clone, PartialEq)]
226pub enum NullActionExt {}
227
228impl fmt::Display for NullActionExt {
229    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
230        Ok(())
231    }
232}
233
234impl std::str::FromStr for NullActionExt {
235    type Err = ();
236
237    fn from_str(_: &str) -> Result<Self, Self::Err> {
238        Err(())
239    }
240}
241
242// --------------- ACTIONS ---------------
243pub use arrayvec::ArrayVec;
244
245#[derive(Debug, Clone, PartialEq)]
246pub struct Actions<A: ActionExt = NullActionExt>(pub ArrayVec<Action<A>, MAX_ACTIONS>);
247
248impl<A: ActionExt> Default for Actions<A> {
249    fn default() -> Self {
250        Self(ArrayVec::new())
251    }
252}
253
254macro_rules! repeat_impl {
255    ($($len:expr),*) => {
256        $(
257            impl<A: ActionExt> From<[Action<A>; $len]> for Actions<A> {
258                fn from(arr: [Action<A>; $len]) -> Self {
259                    Actions(ArrayVec::from_iter(arr))
260                }
261            }
262
263            impl<A: ActionExt> From<[A; $len]> for Actions<A> {
264                fn from(arr: [A; $len]) -> Self {
265                    Actions(arr.into_iter().map(Action::Custom).collect())
266                }
267            }
268        )*
269    }
270}
271impl<A: ActionExt> From<[Action<A>; 0]> for Actions<A> {
272    fn from(empty: [Action<A>; 0]) -> Self {
273        Actions(ArrayVec::from_iter(empty))
274    }
275}
276repeat_impl!(1, 2, 3, 4, 5, 6);
277
278impl<A: ActionExt> From<Action<A>> for Actions<A> {
279    fn from(action: Action<A>) -> Self {
280        acs![action]
281    }
282}
283// no conflict because Action is local type
284impl<A: ActionExt> From<A> for Actions<A> {
285    fn from(action: A) -> Self {
286        acs![Action::Custom(action)]
287    }
288}
289
290// ---------- SERDE ----------------
291
292impl<A: ActionExt + Display> serde::Serialize for Action<A> {
293    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
294    where
295        S: serde::Serializer,
296    {
297        serializer.serialize_str(&self.to_string())
298    }
299}
300
301impl<'de, A: ActionExt + FromStr> Deserialize<'de> for Actions<A> {
302    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303    where
304        D: serde::Deserializer<'de>,
305    {
306        let helper = StringOrVec::deserialize(deserializer)?;
307        let strings = match helper {
308            StringOrVec::String(s) => vec![s],
309            StringOrVec::Vec(v) => v,
310        };
311
312        if strings.len() > MAX_ACTIONS {
313            return Err(serde::de::Error::custom(format!(
314                "Too many actions, max is {MAX_ACTIONS}."
315            )));
316        }
317
318        let mut actions = ArrayVec::new();
319        for s in strings {
320            let action = Action::from_str(&s).map_err(serde::de::Error::custom)?;
321            actions.push(action);
322        }
323
324        Ok(Actions(actions))
325    }
326}
327
328impl<A: ActionExt + Display> Serialize for Actions<A> {
329    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
330    where
331        S: Serializer,
332    {
333        match self.0.len() {
334            1 => serializer.serialize_str(&self.0[0].to_string()),
335            _ => {
336                let strings: Vec<String> = self.0.iter().map(|a| a.to_string()).collect();
337                strings.serialize(serializer)
338            }
339        }
340    }
341}
342
343// ----- action serde
344enum_from_str_display!(
345    units:
346    Select, Deselect, Toggle, CycleAll, ClearSelections, Accept,
347
348    PageDown, PageUp, ScrollLeft, ScrollRight,
349
350    ToggleWrap, TogglePreviewWrap, CyclePreview, PreviewJump,
351
352    PreviewHalfPageUp, PreviewHalfPageDown,
353
354    ForwardChar,BackwardChar, ForwardWord, BackwardWord, DeleteChar, DeleteWord, DeleteLineStart, DeleteLineEnd, Cancel, Redraw, NextColumn, PrevColumn, PrintKey;
355
356    tuples:
357    Execute, ExecuteSilent, Become, Preview,
358    SetQuery, Pos, QueryPos, SwitchColumn, Store;
359
360    defaults:
361    (Up, 1), (Down, 1), (PreviewUp, 1), (PreviewDown, 1), (Quit, 1), (Overlay, 0), (Print, String::new()), (Help, String::new()), (Reload, String::new()), (PreviewScroll, 1), (PreviewHScroll, 1), (HScroll, 0), (VScroll, 0);
362
363    options:
364    SwitchPreview, SetPreview, ToggleColumn, ShowColumn
365);
366
367macro_rules! enum_from_str_display {
368    (
369        units: $($unit:ident),*;
370        tuples: $($tuple:ident),*;
371        defaults: $(($default:ident, $default_value:expr)),*;
372        options: $($optional:ident),*
373    ) => {
374        impl<A: ActionExt + Display> std::fmt::Display for Action<A> {
375            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376                match self {
377                    $( Self::$unit => write!(f, stringify!($unit)), )*
378
379                    $( Self::$tuple(inner) => write!(f, concat!(stringify!($tuple), "({})"), inner), )*
380
381                    $( Self::$default(inner) => {
382                        if *inner == $default_value {
383                            write!(f, stringify!($default))
384                        } else {
385                            write!(f, concat!(stringify!($default), "({})"), inner)
386                        }
387                    }, )*
388
389                    $( Self::$optional(opt) => {
390                        if let Some(inner) = opt {
391                            write!(f, concat!(stringify!($optional), "({})"), inner)
392                        } else {
393                            write!(f, stringify!($optional))
394                        }
395                    }, )*
396
397                    Self::Custom(inner) => {
398                        write!(f, "{}", inner.to_string())
399                    }
400                    Self::Char(c) => {
401                        write!(f, "{c}")
402                    }
403                    Self::Semantic(s) => {
404                        write!(f, "{}", s)
405                    }
406                }
407            }
408        }
409
410        impl<A: ActionExt + FromStr> std::str::FromStr for Action<A> {
411            type Err = String;
412
413            fn from_str(s: &str) -> Result<Self, Self::Err> {
414                let s = s.trim();
415                if let Ok(x) = s.parse::<A>() {
416                    return Ok(Self::Custom(x))
417                }
418
419                if let Some(rest) = s.strip_prefix("::") {
420                    return Ok(Self::Semantic(rest.to_string()));
421                }
422
423                let (name, data) = if let Some(pos) = s.find('(') {
424                    if s.ends_with(')') {
425                        (&s[..pos], Some(&s[pos + 1..s.len() - 1]))
426                    } else {
427                        (s, None)
428                    }
429                } else {
430                    (s, None)
431                };
432
433                match name {
434                    $( n if n.eq_ignore_ascii_case(stringify!($unit)) => {
435                        if data.is_some() {
436                            Err(format!("Unexpected data for unit variant {}", name))
437                        } else {
438                            Ok(Self::$unit)
439                        }
440                    }, )*
441
442                    $( n if n.eq_ignore_ascii_case(stringify!($tuple)) => {
443                        let d = data
444                        .ok_or_else(|| format!("Missing data for {}", stringify!($tuple)))?
445                        .parse()
446                        .map_err(|_| format!("Invalid data for {}", stringify!($tuple)))?;
447                        Ok(Self::$tuple(d))
448                    }, )*
449
450                    $( n if n.eq_ignore_ascii_case(stringify!($default)) => {
451                        let d = match data {
452                            Some(val) => val
453                            .parse()
454                            .map_err(|_| format!("Invalid data for {}", stringify!($default)))?,
455                            None => $default_value,
456                        };
457                        Ok(Self::$default(d))
458                    }, )*
459
460                    $( n if n.eq_ignore_ascii_case(stringify!($optional)) => {
461                        let d = match data {
462                            Some(val) if !val.is_empty() => {
463                                Some(
464                                    val.parse()
465                                    .map_err(|_| format!("Invalid data for {}", stringify!($optional)))?,
466                                )
467                            }
468                            _ => None,
469                        };
470                        Ok(Self::$optional(d))
471                    }, )*
472
473                    _ => Err(format!("Unknown action: {}.", s)),
474                }
475            }
476        }
477    };
478}
479use enum_from_str_display;
480
481impl<A: ActionExt> IntoIterator for Actions<A> {
482    type Item = Action<A>;
483    type IntoIter = <ArrayVec<Action<A>, MAX_ACTIONS> as IntoIterator>::IntoIter;
484
485    fn into_iter(self) -> Self::IntoIter {
486        self.0.into_iter()
487    }
488}
489
490impl<'a, A: ActionExt> IntoIterator for &'a Actions<A> {
491    type Item = &'a Action<A>;
492    type IntoIter = <&'a ArrayVec<Action<A>, MAX_ACTIONS> as IntoIterator>::IntoIter;
493
494    fn into_iter(self) -> Self::IntoIter {
495        self.0.iter()
496    }
497}
498
499impl<A: ActionExt> FromIterator<Action<A>> for Actions<A> {
500    fn from_iter<T: IntoIterator<Item = Action<A>>>(iter: T) -> Self {
501        let mut inner = ArrayVec::<Action<A>, MAX_ACTIONS>::new();
502        inner.extend(iter);
503        Actions(inner)
504    }
505}
506
507use std::ops::{Deref, DerefMut};
508
509impl<A: ActionExt> Deref for Actions<A> {
510    type Target = ArrayVec<Action<A>, MAX_ACTIONS>;
511
512    fn deref(&self) -> &Self::Target {
513        &self.0
514    }
515}
516
517impl<A: ActionExt> DerefMut for Actions<A> {
518    fn deref_mut(&mut self) -> &mut Self::Target {
519        &mut self.0
520    }
521}