editor_types/
prelude.rs

1//! # Common set of types used for describing actions
2//!
3//! ## Overview
4//!
5//! These types are used to specify the details of how to execute [Action] and the
6//! more specific actions that it encompasses.
7//!
8//! Usually you will just want to import everything in this module into your application via:
9//!
10//! ```
11//! use editor_types::prelude::*;
12//! ```
13//!
14//! [Action]: crate::Action
15use std::fmt::{self, Debug, Display, Formatter};
16use std::hash::Hash;
17
18use bitflags::bitflags;
19use regex::Regex;
20
21use crate::application::ApplicationWindowId;
22use crate::context::EditContext;
23use crate::util::{
24    is_filename_char,
25    is_filepath_char,
26    is_horizontal_space,
27    is_keyword,
28    is_newline,
29    is_space_char,
30    is_word_char,
31    sort2,
32};
33use crate::*;
34
35/// Specify how to change the case of a string.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub enum Case {
38    /// Make the targeted text uppercase.
39    Upper,
40
41    /// Make the targeted text lowercase.
42    Lower,
43
44    /// Make the first character of the targeted text uppercase, and the rest lowercase.
45    Title,
46
47    /// Toggle the case of each character in the targeted text.
48    Toggle,
49}
50
51/// Specify how to join lines together.
52#[derive(Clone, Copy, Debug, Eq, PartialEq)]
53pub enum JoinStyle {
54    /// Leave whitespace around the join point as-is.
55    NoChange,
56
57    /// Replace whitespace around the join point with a single space.
58    OneSpace,
59
60    /// Always insert a new space at the join point, regardless of whether there's already
61    /// whitespace there.
62    NewSpace,
63}
64
65/// Specify how to insert register contents into a buffer.
66#[derive(Clone, Debug, Eq, PartialEq)]
67pub enum PasteStyle {
68    /// Paste text before the cursor.
69    ///
70    /// ## Example: Using `action!`
71    ///
72    /// ```
73    /// use editor_types::prelude::*;
74    /// use editor_types::{action, Action, InsertTextAction};
75    ///
76    /// let style = PasteStyle::Cursor;
77    /// let paste: Action = action!("insert paste -s cursor");
78    /// assert_eq!(paste, InsertTextAction::Paste(style, Count::Contextual).into());
79    /// ```
80    Cursor,
81
82    /// Paste text before the selection's start, or after its end.
83    ///
84    /// ## Example: Using `action!`
85    ///
86    /// ```
87    /// use editor_types::prelude::*;
88    /// use editor_types::{action, Action, InsertTextAction};
89    ///
90    /// let style = PasteStyle::Side(MoveDir1D::Next);
91    /// let paste: Action = action!("insert paste -s (side -d next)");
92    /// assert_eq!(paste, InsertTextAction::Paste(style, Count::Contextual).into());
93    ///
94    /// let style = PasteStyle::Side(MoveDir1D::Previous);
95    /// let paste: Action = action!("insert paste -s (side -d prev)");
96    /// assert_eq!(paste, InsertTextAction::Paste(style, Count::Contextual).into());
97    /// ```
98    Side(MoveDir1D),
99
100    /// Replace selected text with register contents.
101    ///
102    /// ## Example: Using `action!`
103    ///
104    /// ```
105    /// use editor_types::prelude::*;
106    /// use editor_types::{action, Action, InsertTextAction};
107    ///
108    /// let style = PasteStyle::Replace;
109    /// let paste: Action = action!("insert paste -s replace");
110    /// assert_eq!(paste, InsertTextAction::Paste(style, Count::Contextual).into());
111    /// ```
112    Replace,
113}
114
115/// The source to search for completion candidates.
116#[derive(Clone, Debug, Eq, PartialEq)]
117pub enum CompletionScope {
118    /// Only use completion candidates from the current buffer.
119    ///
120    /// ## Example: Using `action!`
121    ///
122    /// ```
123    /// use editor_types::prelude::*;
124    /// use editor_types::{action, Action, EditorAction};
125    ///
126    /// let ct = CompletionType::Line(CompletionScope::Buffer);
127    /// let style = CompletionStyle::Prefix;
128    /// let display = CompletionDisplay::List;
129    /// let act: Action = EditorAction::Complete(style, ct, display).into();
130    /// assert_eq!(act, action!("complete -s prefix -T (line buffer) -D list"));
131    /// ```
132    Buffer,
133
134    /// Use completion candidates available from all buffers.
135    ///
136    /// ## Example: Using `action!`
137    ///
138    /// ```
139    /// use editor_types::prelude::*;
140    /// use editor_types::{action, Action, EditorAction};
141    ///
142    /// let ct = CompletionType::Line(CompletionScope::Global);
143    /// let style = CompletionStyle::Prefix;
144    /// let display = CompletionDisplay::List;
145    /// let act: Action = EditorAction::Complete(style, ct, display).into();
146    /// assert_eq!(act, action!("complete -s prefix -T (line global) -D list"));
147    /// ```
148    Global,
149}
150
151/// What type of phrase we are completing.
152#[derive(Clone, Debug, Eq, PartialEq)]
153pub enum CompletionStyle {
154    /// Navigate through the list of completion candidates.
155    ///
156    /// ## Example: Using `action!`
157    ///
158    /// ```
159    /// use editor_types::prelude::*;
160    /// use editor_types::{action, Action, EditorAction};
161    ///
162    /// let ct = CompletionType::Auto;
163    /// let style = CompletionStyle::List(MoveDir1D::Next);
164    /// let display = CompletionDisplay::List;
165    /// let act: Action = EditorAction::Complete(style, ct, display).into();
166    /// assert_eq!(act, action!("complete -s (list next) -T auto -D list"));
167    /// ```
168    List(MoveDir1D),
169
170    /// Generate completion candidates, but don't select any from the list.
171    ///
172    /// This is most helpful for keybindings that allow the user to get a
173    /// list of potential candidates that they can read through without
174    /// actually picking any, in case they don't know what the first
175    /// character to type is.
176    ///
177    /// ## Example: Using `action!`
178    ///
179    /// ```
180    /// use editor_types::prelude::*;
181    /// use editor_types::{action, Action, EditorAction};
182    ///
183    /// let ct = CompletionType::Auto;
184    /// let style = CompletionStyle::None;
185    /// let display = CompletionDisplay::List;
186    /// let act: Action = EditorAction::Complete(style, ct, display).into();
187    /// assert_eq!(act, action!("complete -s none -T auto -D list"));
188    /// ```
189    None,
190
191    /// Complete only the longest common prefix from the completion candidates.
192    ///
193    /// ## Example: Using `action!`
194    ///
195    /// ```
196    /// use editor_types::prelude::*;
197    /// use editor_types::{action, Action, EditorAction};
198    ///
199    /// let ct = CompletionType::Auto;
200    /// let style = CompletionStyle::Prefix;
201    /// let display = CompletionDisplay::List;
202    /// let act: Action = EditorAction::Complete(style, ct, display).into();
203    /// assert_eq!(act, action!("complete -s prefix -T auto -D list"));
204    /// ```
205    Prefix,
206
207    /// If there is only a single completion candidate, select it.
208    ///
209    /// ## Example: Using `action!`
210    ///
211    /// ```
212    /// use editor_types::prelude::*;
213    /// use editor_types::{action, Action, EditorAction};
214    ///
215    /// let ct = CompletionType::Auto;
216    /// let style = CompletionStyle::Single;
217    /// let display = CompletionDisplay::List;
218    /// let act: Action = EditorAction::Complete(style, ct, display).into();
219    /// assert_eq!(act, action!("complete -s single -T auto -D list"));
220    /// ```
221    Single,
222}
223
224/// What type of phrase we are completing.
225///
226/// Typically, most editors use the cursor's context to determine what to
227/// complete. In those cases, [CompletionType::Auto] is sufficient, but other
228/// variants are provided here to accomodate keybindings that specifically
229/// complete context-independent values.
230#[derive(Clone, Debug, Eq, PartialEq)]
231pub enum CompletionType {
232    /// Determine what to complete by the buffer context.
233    ///
234    /// ## Example: Using `action!`
235    ///
236    /// ```
237    /// use editor_types::prelude::*;
238    /// use editor_types::{action, Action, EditorAction};
239    ///
240    /// let ct = CompletionType::Auto;
241    /// let style = CompletionStyle::Prefix;
242    /// let display = CompletionDisplay::List;
243    /// let act: Action = EditorAction::Complete(style, ct, display).into();
244    /// assert_eq!(act, action!("complete -s prefix -T auto -D list"));
245    /// ```
246    Auto,
247
248    /// Complete a filename.
249    ///
250    /// ## Example: Using `action!`
251    ///
252    /// ```
253    /// use editor_types::prelude::*;
254    /// use editor_types::{action, Action, EditorAction};
255    ///
256    /// let ct = CompletionType::File;
257    /// let style = CompletionStyle::Prefix;
258    /// let display = CompletionDisplay::List;
259    /// let act: Action = EditorAction::Complete(style, ct, display).into();
260    /// assert_eq!(act, action!("complete -s prefix -T file -D list"));
261    /// ```
262    File,
263
264    /// Complete the rest of the line.
265    ///
266    /// ## Example: Using `action!`
267    ///
268    /// ```
269    /// use editor_types::prelude::*;
270    /// use editor_types::{action, Action, EditorAction};
271    ///
272    /// let ct = CompletionType::Line(CompletionScope::Global);
273    /// let style = CompletionStyle::Prefix;
274    /// let display = CompletionDisplay::List;
275    /// let act: Action = EditorAction::Complete(style, ct, display).into();
276    /// assert_eq!(act, action!("complete -s prefix -T (line global) -D list"));
277    /// ```
278    Line(CompletionScope),
279
280    /// Complete the current word.
281    ///
282    /// ## Example: Using `action!`
283    ///
284    /// ```
285    /// use editor_types::prelude::*;
286    /// use editor_types::{action, Action, EditorAction};
287    ///
288    /// let ct = CompletionType::Word(CompletionScope::Buffer);
289    /// let style = CompletionStyle::Prefix;
290    /// let display = CompletionDisplay::List;
291    /// let act: Action = EditorAction::Complete(style, ct, display).into();
292    /// assert_eq!(act, action!("complete -s prefix -T (word buffer) -D list"));
293    /// ```
294    Word(CompletionScope),
295}
296
297/// How to display completion candidates.
298#[derive(Clone, Debug, Eq, PartialEq)]
299pub enum CompletionDisplay {
300    /// Don't display candidates.
301    ///
302    /// This method of displaying completions is most useful for contexts where
303    /// users don't expect to see possible completions and just want to cycle
304    /// through what's available, such as completing filenames in a command bar.
305    ///
306    /// ## Example: Using `action!`
307    ///
308    /// ```
309    /// use editor_types::prelude::*;
310    /// use editor_types::{action, Action, EditorAction};
311    ///
312    /// let ct = CompletionType::Auto;
313    /// let style = CompletionStyle::Prefix;
314    /// let display = CompletionDisplay::None;
315    /// let act: Action = EditorAction::Complete(style, ct, display).into();
316    /// assert_eq!(act, action!("complete -s prefix -T auto -D none"));
317    /// ```
318    None,
319
320    /// Display candidates in a bar above the command bar.
321    ///
322    /// ## Example: Using `action!`
323    ///
324    /// ```
325    /// use editor_types::prelude::*;
326    /// use editor_types::{action, Action, EditorAction};
327    ///
328    /// let ct = CompletionType::Auto;
329    /// let style = CompletionStyle::Prefix;
330    /// let display = CompletionDisplay::Bar;
331    /// let act: Action = EditorAction::Complete(style, ct, display).into();
332    /// assert_eq!(act, action!("complete -s prefix -T auto -D bar"));
333    /// ```
334    Bar,
335
336    /// Display candidates in a pop-up list.
337    ///
338    /// ## Example: Using `action!`
339    ///
340    /// ```
341    /// use editor_types::prelude::*;
342    /// use editor_types::{action, Action, EditorAction};
343    ///
344    /// let ct = CompletionType::Auto;
345    /// let style = CompletionStyle::Prefix;
346    /// let display = CompletionDisplay::List;
347    /// let act: Action = EditorAction::Complete(style, ct, display).into();
348    /// assert_eq!(act, action!("complete -s prefix -T auto -D list"));
349    /// ```
350    List,
351}
352
353/// Specify what is targeted by an editing action.
354#[derive(Clone, Debug, Eq, PartialEq)]
355#[non_exhaustive]
356pub enum EditTarget {
357    /// Move to one of the sides of a range.
358    Boundary(RangeType, bool, MoveTerminus, Count),
359
360    /// Target the current cursor position.
361    CurrentPosition,
362
363    /// Move to the line and column of a [Mark].
364    CharJump(Specifier<Mark>),
365
366    /// Move to the first word of the line that [Mark] is on.
367    LineJump(Specifier<Mark>),
368
369    /// Target the text between the current cursor position and the end of a motion.
370    Motion(MoveType, Count),
371
372    /// Target a range of text around the cursor.
373    ///
374    /// [bool] indicates if this is an inclusive range, when applicable to the [RangeType].
375    Range(RangeType, bool, Count),
376
377    /// Target the text between the current cursor position and the end of a search.
378    ///
379    /// The [MoveDirMod] parameter modifies the search direction.
380    Search(SearchType, MoveDirMod, Count),
381
382    /// Target the visually selected text.
383    Selection,
384}
385
386impl EditTarget {
387    /// Returns `true` if this is a target that causes cursor positions to be saved to
388    /// [PositionList::JumpList].
389    pub fn is_jumping(&self) -> bool {
390        match self {
391            EditTarget::Boundary(..) => true,
392            EditTarget::CurrentPosition => false,
393            EditTarget::CharJump(_) => true,
394            EditTarget::LineJump(_) => true,
395            EditTarget::Motion(mt, _) => mt.is_jumping(),
396            EditTarget::Range(..) => true,
397            EditTarget::Search(st, ..) => st.is_jumping(),
398            EditTarget::Selection => false,
399        }
400    }
401}
402
403impl From<MoveType> for EditTarget {
404    fn from(mt: MoveType) -> Self {
405        EditTarget::Motion(mt, Count::Contextual)
406    }
407}
408
409impl From<RangeType> for EditTarget {
410    fn from(mt: RangeType) -> Self {
411        EditTarget::Range(mt, true, Count::Contextual)
412    }
413}
414
415/// Determines where to leave the cursor after editing text.
416#[derive(Clone, Copy, Debug, Eq, PartialEq)]
417pub enum CursorEnd {
418    /// Keep the current cursor position as best as possible.
419    Keep,
420
421    /// Place the cursor at the start of the [EditTarget].
422    Start,
423
424    /// Place the cursor at the end of the [EditTarget].
425    End,
426
427    /// Select from the start to the end of the [EditTarget].
428    Selection,
429
430    /// Use the default cursor end position for the operation.
431    Auto,
432}
433
434/// Description of a textual range within a buffer.
435#[derive(Clone, Debug, Eq, PartialEq)]
436pub struct EditRange<Cursor> {
437    /// The start of the range.
438    pub start: Cursor,
439
440    /// The end of the range.
441    pub end: Cursor,
442
443    /// The default shape to interpret the range as. This might be overriden by
444    /// [EditContext::get_target_shape].
445    pub shape: TargetShape,
446
447    /// Whether to include the character at the end Cursor when interpreted as a CharWise range.
448    pub inclusive: bool,
449}
450
451impl<Cursor: Ord> EditRange<Cursor> {
452    /// Create a new editing range.
453    pub fn new(a: Cursor, b: Cursor, shape: TargetShape, inclusive: bool) -> Self {
454        let (start, end) = sort2(a, b);
455
456        EditRange { start, end, shape, inclusive }
457    }
458
459    /// Create a new inclusive editing range.
460    pub fn inclusive(a: Cursor, b: Cursor, shape: TargetShape) -> Self {
461        Self::new(a, b, shape, true)
462    }
463
464    /// Create a new exclusive editing range.
465    pub fn exclusive(a: Cursor, b: Cursor, shape: TargetShape) -> Self {
466        Self::new(a, b, shape, false)
467    }
468}
469
470/// Different action sequences that can be repeated.
471#[derive(Clone, Debug, Eq, Hash, PartialEq)]
472pub enum RepeatType {
473    /// A sequence of changes made to a buffer.
474    ///
475    /// ## Example: Using `action!`
476    ///
477    /// ```
478    /// use editor_types::prelude::*;
479    /// use editor_types::{action, Action};
480    ///
481    /// let rep: Action = action!("repeat -s edit-sequence");
482    /// assert_eq!(rep, Action::Repeat(RepeatType::EditSequence));
483    /// ```
484    EditSequence,
485
486    /// The last [Action] done.
487    ///
488    /// ## Example: Using `action!`
489    ///
490    /// ```
491    /// use editor_types::prelude::*;
492    /// use editor_types::{action, Action};
493    ///
494    /// let rep: Action = action!("repeat -s last-action");
495    /// assert_eq!(rep, Action::Repeat(RepeatType::LastAction));
496    /// ```
497    LastAction,
498
499    /// The last selection resize made in a buffer.
500    ///
501    /// ## Example: Using `action!`
502    ///
503    /// ```
504    /// use editor_types::prelude::*;
505    /// use editor_types::{action, Action};
506    ///
507    /// let rep: Action = action!("repeat -s last-selection");
508    /// assert_eq!(rep, Action::Repeat(RepeatType::LastSelection));
509    /// ```
510    LastSelection,
511}
512
513/// Specify a range within the text around the current cursor position.
514#[derive(Clone, Debug, Eq, PartialEq)]
515pub enum SearchType {
516    /// Search for the character indicated by [EditContext::get_search_char].
517    ///
518    /// [bool] controls whether the search should continue across line boundaries.
519    Char(bool),
520
521    /// Search for a regular expression.
522    Regex,
523
524    /// Search for the word currently under the cursor, and update the last [CommandType::Search]
525    /// value in the application's register store.
526    ///
527    /// [bool] controls whether matches should be checked for using word boundaries.
528    Word(WordStyle, bool),
529}
530
531impl SearchType {
532    /// Returns `true` if this is an inclusive motion.
533    pub fn is_inclusive_motion(&self) -> bool {
534        match self {
535            SearchType::Char(..) => true,
536            SearchType::Regex => false,
537            SearchType::Word(..) => false,
538        }
539    }
540
541    /// Returns `true` if this is a search that causes cursor positions to be saved to
542    /// [PositionList::JumpList].
543    fn is_jumping(&self) -> bool {
544        match self {
545            SearchType::Char(..) => false,
546            SearchType::Regex => true,
547            SearchType::Word(..) => true,
548        }
549    }
550}
551
552/// The different ways of grouping a buffer's contents into words.
553#[derive(Clone, Debug, Eq, PartialEq)]
554pub enum WordStyle {
555    /// A run of alphanumeric characters.
556    ///
557    /// ## Example: Using `action!`
558    ///
559    /// ```
560    /// use editor_types::prelude::*;
561    /// use editor_types::{action, Action};
562    ///
563    /// let style = WordStyle::AlphaNum;
564    /// let kw: Action = Action::KeywordLookup(style.into());
565    ///
566    /// // All of these are equivalent:
567    /// assert_eq!(kw, action!("keyword-lookup -t (word alphanum)"));
568    /// assert_eq!(kw, action!("keyword-lookup -t (word alpha-num)"));
569    /// ```
570    AlphaNum,
571
572    /// A sequence of non-blank characters.
573    ///
574    /// An empty line is also a Big word. Vim calls this a `WORD`.
575    ///
576    /// ## Example: Using `action!`
577    ///
578    /// ```
579    /// use editor_types::prelude::*;
580    /// use editor_types::{action, Action};
581    ///
582    /// let style = WordStyle::Big;
583    /// let kw: Action = Action::KeywordLookup(style.into());
584    /// assert_eq!(kw, action!("keyword-lookup -t (word big)"));
585    /// ```
586    Big,
587
588    /// A sequence of characters that match a test function.
589    CharSet(fn(char) -> bool),
590
591    /// A name of a directory or file.
592    ///
593    /// ## Example: Using `action!`
594    ///
595    /// ```
596    /// use editor_types::prelude::*;
597    /// use editor_types::{action, Action};
598    ///
599    /// let style = WordStyle::FileName;
600    /// let kw: Action = Action::KeywordLookup(style.into());
601    ///
602    /// // All of these are equivalent:
603    /// assert_eq!(kw, action!("keyword-lookup -t (word filename)"));
604    /// assert_eq!(kw, action!("keyword-lookup -t (word file-name)"));
605    /// ```
606    FileName,
607
608    /// A path to a directory or file.
609    ///
610    /// ## Example: Using `action!`
611    ///
612    /// ```
613    /// use editor_types::prelude::*;
614    /// use editor_types::{action, Action};
615    ///
616    /// let style = WordStyle::FilePath;
617    /// let kw: Action = Action::KeywordLookup(style.into());
618    ///
619    /// // All of these are equivalent:
620    /// assert_eq!(kw, action!("keyword-lookup -t (word filepath)"));
621    /// assert_eq!(kw, action!("keyword-lookup -t (word file-path)"));
622    /// ```
623    FilePath,
624
625    /// Either a sequence of alphanumeric characters and underscores, or a sequence of other
626    /// non-blank characters.
627    ///
628    /// An empty line is also a Little word.
629    ///
630    /// ## Example: Using `action!`
631    ///
632    /// ```
633    /// use editor_types::prelude::*;
634    /// use editor_types::{action, Action};
635    ///
636    /// let style = WordStyle::Little;
637    /// let kw: Action = Action::KeywordLookup(style.into());
638    /// assert_eq!(kw, action!("keyword-lookup -t (word little)"));
639    /// ```
640    Little,
641
642    /// A run of non-alphanumeric characters.
643    ///
644    /// ## Example: Using `action!`
645    ///
646    /// ```
647    /// use editor_types::prelude::*;
648    /// use editor_types::{action, Action};
649    ///
650    /// let style = WordStyle::NonAlphaNum;
651    /// let kw: Action = Action::KeywordLookup(style.into());
652    ///
653    /// // All of these are equivalent:
654    /// assert_eq!(kw, action!("keyword-lookup -t (word non-alphanum)"));
655    /// assert_eq!(kw, action!("keyword-lookup -t (word non-alphanumeric)"));
656    /// assert_eq!(kw, action!("keyword-lookup -t (word nonalphanum)"));
657    /// assert_eq!(kw, action!("keyword-lookup -t (word nonalphanumeric)"));
658    /// ```
659    NonAlphaNum,
660
661    /// A run of digits in the given base, with an optional leading hyphen.
662    ///
663    /// ## Example: Using `action!`
664    ///
665    /// ```
666    /// use editor_types::prelude::*;
667    /// use editor_types::{action, Action};
668    ///
669    /// let style = WordStyle::Number(Radix::Decimal);
670    /// let kw: Action = Action::KeywordLookup(style.into());
671    /// assert_eq!(kw, action!("keyword-lookup -t (word radix decimal)"));
672    /// ```
673    Number(Radix),
674
675    /// A run of blank characters.
676    ///
677    /// [bool] controls whether this crosses line boundaries.
678    ///
679    /// ## Example: Using `action!`
680    ///
681    /// ```
682    /// use editor_types::prelude::*;
683    /// use editor_types::{action, Action};
684    ///
685    /// let style = WordStyle::Whitespace(true);
686    /// let kw: Action = Action::KeywordLookup(style.into());
687    /// assert_eq!(kw, action!("keyword-lookup -t (word whitespace -w true)"));
688    ///
689    /// let style = WordStyle::Whitespace(false);
690    /// let kw: Action = Action::KeywordLookup(style.into());
691    /// assert_eq!(kw, action!("keyword-lookup -t (word whitespace -w false)"));
692    /// ```
693    Whitespace(bool),
694}
695
696impl WordStyle {
697    /// Whether this [WordStyle] can ever contain the character `c`.
698    pub fn contains(&self, c: char) -> bool {
699        match self {
700            WordStyle::AlphaNum => is_word_char(c),
701            WordStyle::NonAlphaNum => !is_word_char(c),
702            WordStyle::Big => !is_space_char(c),
703            WordStyle::CharSet(f) => f(c),
704            WordStyle::FileName => is_filename_char(c),
705            WordStyle::FilePath => is_filepath_char(c),
706            WordStyle::Little => is_word_char(c) || is_keyword(c),
707            WordStyle::Number(radix) => radix.contains(c),
708            WordStyle::Whitespace(true) => is_space_char(c),
709            WordStyle::Whitespace(false) => is_horizontal_space(c),
710        }
711    }
712}
713
714impl BoundaryTest for WordStyle {
715    fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool {
716        match self {
717            WordStyle::AlphaNum => {
718                if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
719                    // Last character is counted when moving forward.
720                    return true;
721                } else if let Some(before) = ctx.before {
722                    let befwc = is_word_char(before);
723                    let curwc = is_word_char(ctx.current);
724
725                    return !befwc && curwc;
726                } else {
727                    // First character is always counted.
728                    return true;
729                }
730            },
731            WordStyle::NonAlphaNum => {
732                if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
733                    // Last character is counted when moving forward.
734                    return true;
735                } else if let Some(before) = ctx.before {
736                    let befwc = is_word_char(before);
737                    let curwc = is_word_char(ctx.current);
738
739                    return befwc && !curwc;
740                } else {
741                    // First character is always counted.
742                    return true;
743                }
744            },
745            WordStyle::Big => {
746                if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
747                    // Last character is counted when moving forward.
748                    return true;
749                } else if let Some(before) = ctx.before {
750                    let befws = is_space_char(before);
751                    let curws = is_space_char(ctx.current);
752                    let curnl = is_newline(ctx.current);
753
754                    // The final word beginning is calculated differently during an operation.
755                    let last = !ctx.motion && ctx.count == 1;
756
757                    return (last && curnl) || (befws && !curws);
758                } else {
759                    // First character is always counted.
760                    return true;
761                }
762            },
763            WordStyle::CharSet(f) => {
764                if let Some(before) = ctx.before {
765                    f(ctx.current) && !f(before)
766                } else {
767                    f(ctx.current)
768                }
769            },
770            WordStyle::FileName => {
771                if let Some(before) = ctx.before {
772                    is_filename_char(ctx.current) && !is_filename_char(before)
773                } else {
774                    is_filename_char(ctx.current)
775                }
776            },
777            WordStyle::FilePath => {
778                if let Some(before) = ctx.before {
779                    is_filepath_char(ctx.current) && !is_filepath_char(before)
780                } else {
781                    is_filepath_char(ctx.current)
782                }
783            },
784            WordStyle::Little => {
785                if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
786                    // Last character is counted when moving forward.
787                    return true;
788                } else if let Some(before) = ctx.before {
789                    let befwc = is_word_char(before);
790                    let befkw = is_keyword(before);
791                    let curwc = is_word_char(ctx.current);
792                    let curkw = is_keyword(ctx.current);
793                    let curnl = is_newline(ctx.current);
794
795                    // The final word beginning is calculated differently during an operation.
796                    let last = !ctx.motion && ctx.count == 1;
797
798                    return (last && curnl) ||
799                        (befwc && curkw) ||
800                        (befkw && curwc) ||
801                        (!befwc && curwc) ||
802                        (!befkw && curkw);
803                } else {
804                    // First character is always counted.
805                    return true;
806                }
807            },
808            WordStyle::Number(radix) => {
809                let cn = radix.contains(ctx.current);
810
811                if ctx.current == '-' {
812                    // A hyphen is only the start of a number if a digit follows it.
813                    matches!(ctx.after, Some(c) if radix.contains(c))
814                } else if let Some(before) = ctx.before {
815                    // Not preceded by a hyphen or digit.
816                    cn && before != '-' && !radix.contains(before)
817                } else {
818                    // First character counts if it's a digit.
819                    cn
820                }
821            },
822            WordStyle::Whitespace(multiline) => {
823                let f = if *multiline {
824                    is_space_char
825                } else {
826                    is_horizontal_space
827                };
828
829                return f(ctx.current) && matches!(ctx.before, Some(c) if !f(c));
830            },
831        }
832    }
833
834    fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool {
835        match self {
836            WordStyle::AlphaNum => {
837                if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
838                    // First character is counted when moving back.
839                    return true;
840                } else if let Some(after) = ctx.after {
841                    let curwc = is_word_char(ctx.current);
842                    let aftwc = is_word_char(after);
843
844                    return curwc && !aftwc;
845                } else {
846                    // Last character is always counted.
847                    return true;
848                }
849            },
850            WordStyle::NonAlphaNum => {
851                if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
852                    // First character is counted when moving back.
853                    return true;
854                } else if let Some(after) = ctx.after {
855                    let curwc = is_word_char(ctx.current);
856                    let aftwc = is_word_char(after);
857
858                    return !curwc && aftwc;
859                } else {
860                    // Last character is always counted.
861                    return true;
862                }
863            },
864            WordStyle::Big => {
865                if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
866                    // First character is counted when moving back.
867                    return true;
868                } else if let Some(after) = ctx.after {
869                    !is_space_char(ctx.current) && is_space_char(after)
870                } else {
871                    // Last character is always a word ending.
872                    return true;
873                }
874            },
875            WordStyle::CharSet(f) => {
876                if let Some(after) = ctx.after {
877                    f(ctx.current) && !f(after)
878                } else {
879                    f(ctx.current)
880                }
881            },
882            WordStyle::FileName => {
883                if let Some(after) = ctx.after {
884                    is_filename_char(ctx.current) && !is_filename_char(after)
885                } else {
886                    is_filename_char(ctx.current)
887                }
888            },
889            WordStyle::FilePath => {
890                if let Some(after) = ctx.after {
891                    is_filepath_char(ctx.current) && !is_filepath_char(after)
892                } else {
893                    is_filepath_char(ctx.current)
894                }
895            },
896            WordStyle::Little => {
897                if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
898                    // First character is counted when moving back.
899                    return true;
900                } else if let Some(after) = ctx.after {
901                    let curwc = is_word_char(ctx.current);
902                    let curkw = is_keyword(ctx.current);
903                    let aftwc = is_word_char(after);
904                    let aftkw = is_keyword(after);
905
906                    return (curwc && aftkw) ||
907                        (curkw && aftwc) ||
908                        (curwc && !aftwc) ||
909                        (curkw && !aftkw);
910                } else {
911                    // Last character is always counted.
912                    return true;
913                }
914            },
915            WordStyle::Number(radix) => {
916                if let Some(after) = ctx.after {
917                    return radix.contains(ctx.current) && !radix.contains(after);
918                } else {
919                    return radix.contains(ctx.current);
920                }
921            },
922            WordStyle::Whitespace(multiline) => {
923                let f = if *multiline {
924                    is_space_char
925                } else {
926                    is_horizontal_space
927                };
928
929                return f(ctx.current) && matches!(ctx.after, Some(c) if !f(c));
930            },
931        }
932    }
933}
934
935impl From<Radix> for WordStyle {
936    fn from(radix: Radix) -> Self {
937        WordStyle::Number(radix)
938    }
939}
940
941/// Specify the base for a number.
942#[derive(Clone, Copy, Debug, Eq, PartialEq)]
943pub enum Radix {
944    /// A base 2 number.
945    /// ## Example: Using `action!`
946    ///
947    /// ```
948    /// use editor_types::prelude::*;
949    /// use editor_types::{action, Action};
950    ///
951    /// let style = WordStyle::Number(Radix::Binary);
952    /// let kw: Action = Action::KeywordLookup(style.into());
953    ///
954    /// // All of these are equivalent:
955    /// assert_eq!(kw, action!("keyword-lookup -t (word radix 2)"));
956    /// assert_eq!(kw, action!("keyword-lookup -t (word radix bin)"));
957    /// assert_eq!(kw, action!("keyword-lookup -t (word radix binary)"));
958    /// ```
959    Binary,
960
961    /// A base 8 number.
962    ///
963    /// ## Example: Using `action!`
964    ///
965    /// ```
966    /// use editor_types::prelude::*;
967    /// use editor_types::{action, Action};
968    ///
969    /// let style = WordStyle::Number(Radix::Octal);
970    /// let kw: Action = Action::KeywordLookup(style.into());
971    ///
972    /// // All of these are equivalent:
973    /// assert_eq!(kw, action!("keyword-lookup -t (word radix 8)"));
974    /// assert_eq!(kw, action!("keyword-lookup -t (word radix oct)"));
975    /// assert_eq!(kw, action!("keyword-lookup -t (word radix octal)"));
976    /// ```
977    Octal,
978
979    /// A base 10 number.
980    ///
981    /// ## Example: Using `action!`
982    ///
983    /// ```
984    /// use editor_types::prelude::*;
985    /// use editor_types::{action, Action};
986    ///
987    /// let style = WordStyle::Number(Radix::Decimal);
988    /// let kw: Action = Action::KeywordLookup(style.into());
989    ///
990    /// // All of these are equivalent:
991    /// assert_eq!(kw, action!("keyword-lookup -t (word radix 10)"));
992    /// assert_eq!(kw, action!("keyword-lookup -t (word radix dec)"));
993    /// assert_eq!(kw, action!("keyword-lookup -t (word radix decimal)"));
994    /// ```
995    Decimal,
996
997    /// A base 16 number.
998    ///
999    /// ## Example: Using `action!`
1000    ///
1001    /// ```
1002    /// use editor_types::prelude::*;
1003    /// use editor_types::{action, Action};
1004    ///
1005    /// let style = WordStyle::Number(Radix::Hexadecimal);
1006    /// let kw: Action = Action::KeywordLookup(style.into());
1007    ///
1008    /// // All of these are equivalent:
1009    /// assert_eq!(kw, action!("keyword-lookup -t (word radix 16)"));
1010    /// assert_eq!(kw, action!("keyword-lookup -t (word radix hex)"));
1011    /// assert_eq!(kw, action!("keyword-lookup -t (word radix hexadecimal)"));
1012    /// ```
1013    Hexadecimal,
1014}
1015
1016impl Radix {
1017    /// Test whether a character is used by this base.
1018    pub fn contains(&self, c: char) -> bool {
1019        match self {
1020            Radix::Binary => c == '0' || c == '1',
1021            Radix::Octal => c >= '0' && c <= '7',
1022            Radix::Decimal => c.is_ascii_digit(),
1023            Radix::Hexadecimal => c.is_ascii_hexdigit(),
1024        }
1025    }
1026}
1027
1028/// Contextual information given while searching for the boundary of a range.
1029pub struct BoundaryTestContext {
1030    /// The current candidate character for the object boundary search.
1031    pub current: char,
1032
1033    /// The character that comes before the candidate in the text.
1034    pub before: Option<char>,
1035
1036    /// The character that comes after the candidate in the text.
1037    pub after: Option<char>,
1038
1039    /// The direction the search is moving in.
1040    pub dir: MoveDir1D,
1041
1042    /// Whether we are performing this search as part of a cursor movement.
1043    pub motion: bool,
1044
1045    /// How many boundaries we have left to find.
1046    pub count: usize,
1047}
1048
1049/// Trait for types which have simple start and end boundaries within a text document.
1050///
1051/// Boundaries are searched for a character at a time, with the previous and following character
1052/// context provided if available.
1053pub trait BoundaryTest {
1054    /// Check whether we are at the beginning of the range.
1055    fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool;
1056
1057    /// Check whether we are at the end of the range.
1058    fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool;
1059
1060    /// Check whether we are at the given side of the range.
1061    fn is_boundary(&self, terminus: MoveTerminus, ctx: &BoundaryTestContext) -> bool {
1062        match terminus {
1063            MoveTerminus::Beginning => self.is_boundary_begin(ctx),
1064            MoveTerminus::End => self.is_boundary_end(ctx),
1065        }
1066    }
1067}
1068
1069/// Specify a range within the text around the current cursor position.
1070#[derive(Clone, Debug, Eq, PartialEq)]
1071#[non_exhaustive]
1072pub enum RangeType {
1073    /// Select from the beginning to the end of a [word](WordStyle).
1074    Word(WordStyle),
1075
1076    /// Select the whole buffer.
1077    Buffer,
1078
1079    /// Select the current paragraph the cursor is in.
1080    Paragraph,
1081
1082    /// Select the current sentence the cursor is in.
1083    Sentence,
1084
1085    /// Select the current line the cursor is on.
1086    Line,
1087
1088    /// Select the current block specified by the start and end characters.
1089    ///
1090    /// When done inclusively, the delimiters are included.
1091    Bracketed(char, char),
1092
1093    /// Select the range enclosed by the next item character.
1094    ///
1095    /// This is the ranged version of [MoveType::ItemMatch].
1096    Item,
1097
1098    /// Select text quoted by [char] around the cursor.
1099    ///
1100    /// When done inclusively, the quote characters are included.
1101    Quote(char),
1102
1103    /// Select the XML block around the cursor.
1104    ///
1105    /// When done inclusively, the opening and closing tags are included.
1106    XmlTag,
1107}
1108
1109/// Specify a movement away from the current cursor position.
1110#[derive(Clone, Debug, Eq, PartialEq)]
1111#[non_exhaustive]
1112pub enum MoveType {
1113    /// Move to a line at a position relative to the buffer.
1114    BufferPos(MovePosition),
1115
1116    /// Move to the column [*n* bytes](Count) into the buffer.
1117    BufferByteOffset,
1118
1119    /// Move to the [*n*<sup>th</sup> line](Count) in the buffer.
1120    BufferLineOffset,
1121
1122    /// Move to the line [*n*%](Count) of the way through the buffer.
1123    BufferLinePercent,
1124
1125    /// Move to the previous or next column [*n* times](Count).
1126    ///
1127    /// The [bool] parameter indicates whether to cross line boundaries.
1128    Column(MoveDir1D, bool),
1129
1130    /// Move to the final non-blank character [*n* lines](Count) away in [MoveDir1D] direction.
1131    FinalNonBlank(MoveDir1D),
1132
1133    /// Move to the first word [*n* lines](Count) away in [MoveDir1D] direction.
1134    FirstWord(MoveDir1D),
1135
1136    /// Move to the matching character of the next item.
1137    ///
1138    /// Items are characters like `(`/`)`, `[`/`]`, `{`/`}`, and so on.
1139    ItemMatch,
1140
1141    /// Move [*n* lines](Count) in [MoveDir1D] direction.
1142    Line(MoveDir1D),
1143
1144    /// Move to the [*n*<sup>th</sup>](Count) column in the current line.
1145    LineColumnOffset,
1146
1147    /// Move to the column [*n*%](Count) of the way through the current line.
1148    LinePercent,
1149
1150    /// Move to a column at a position relative to the current line.
1151    LinePos(MovePosition),
1152
1153    /// Move to the beginning of a word [*n* times](Count) in [MoveDir1D] direction.
1154    WordBegin(WordStyle, MoveDir1D),
1155
1156    /// Move to the end of a word [*n* times](Count) in [MoveDir1D] direction.
1157    WordEnd(WordStyle, MoveDir1D),
1158
1159    /// Move to the beginning of a paragraph [*n* times](Count) in [MoveDir1D] direction.
1160    ParagraphBegin(MoveDir1D),
1161
1162    /// Move to the beginning of a sentence [*n* times](Count) in [MoveDir1D] direction.
1163    SentenceBegin(MoveDir1D),
1164
1165    /// Move to the beginning of a section [*n* times](Count) in [MoveDir1D] direction.
1166    SectionBegin(MoveDir1D),
1167
1168    /// Move to the end of a section [*n* times](Count) in [MoveDir1D] direction.
1169    SectionEnd(MoveDir1D),
1170
1171    /// Move to the first word of a screen line [*n* times](Count) away in [MoveDir1D] direction.
1172    ScreenFirstWord(MoveDir1D),
1173
1174    /// Move [*n* screen lines](Count) in [MoveDir1D] direction.
1175    ScreenLine(MoveDir1D),
1176
1177    /// Move to a column at a position relative to the current screen line.
1178    ScreenLinePos(MovePosition),
1179
1180    /// Move to the first word of the line displayed at a position relative to the viewport.
1181    ViewportPos(MovePosition),
1182}
1183
1184/// Represent movement along a 1-dimensional line.
1185#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1186pub enum MoveDir1D {
1187    /// Move backwards, or to a previous point.
1188    ///
1189    /// ## Example: Using `action!`
1190    ///
1191    /// ```
1192    /// use editor_types::prelude::*;
1193    /// use editor_types::{action, Action, WindowAction};
1194    ///
1195    /// let act: Action = WindowAction::Rotate(MoveDir1D::Previous).into();
1196    ///
1197    /// // All of these are equivalent:
1198    /// assert_eq!(act, action!("window rotate -d previous"));
1199    /// assert_eq!(act, action!("window rotate -d prev"));
1200    /// ```
1201    Previous,
1202
1203    /// Move forwards, or to a following point.
1204    ///
1205    /// ## Example: Using `action!`
1206    ///
1207    /// ```
1208    /// use editor_types::prelude::*;
1209    /// use editor_types::{action, Action, WindowAction};
1210    ///
1211    /// let act: Action = WindowAction::Rotate(MoveDir1D::Next).into();
1212    /// assert_eq!(act, action!("window rotate -d next"));
1213    /// ```
1214    Next,
1215}
1216
1217/// Represent movement along the horizontal or vertical axes.
1218#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1219pub enum MoveDir2D {
1220    /// Move leftwards.
1221    ///
1222    /// ## Example: Using `action!`
1223    ///
1224    /// ```
1225    /// use editor_types::prelude::*;
1226    /// use editor_types::{action, Action, WindowAction};
1227    ///
1228    /// let act: Action = WindowAction::MoveSide(MoveDir2D::Left).into();
1229    /// assert_eq!(act, action!("window move-side -d left"));
1230    /// ```
1231    Left,
1232
1233    /// Move rightwards.
1234    ///
1235    /// ## Example: Using `action!`
1236    ///
1237    /// ```
1238    /// use editor_types::prelude::*;
1239    /// use editor_types::{action, Action, WindowAction};
1240    ///
1241    /// let act: Action = WindowAction::MoveSide(MoveDir2D::Right).into();
1242    /// assert_eq!(act, action!("window move-side -d right"));
1243    /// ```
1244    Right,
1245
1246    /// Move upwards.
1247    ///
1248    /// ## Example: Using `action!`
1249    ///
1250    /// ```
1251    /// use editor_types::prelude::*;
1252    /// use editor_types::{action, Action, WindowAction};
1253    ///
1254    /// let act: Action = WindowAction::MoveSide(MoveDir2D::Up).into();
1255    /// assert_eq!(act, action!("window move-side -d up"));
1256    /// ```
1257    Up,
1258
1259    /// Move downwards.
1260    ///
1261    /// ## Example: Using `action!`
1262    ///
1263    /// ```
1264    /// use editor_types::prelude::*;
1265    /// use editor_types::{action, Action, WindowAction};
1266    ///
1267    /// let act: Action = WindowAction::MoveSide(MoveDir2D::Down).into();
1268    /// assert_eq!(act, action!("window move-side -d down"));
1269    /// ```
1270    Down,
1271}
1272
1273/// Represents the two sides of a range that has no meaningful middle.
1274#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1275pub enum MoveTerminus {
1276    /// The beginning of a range.
1277    Beginning,
1278
1279    /// The end of a range.
1280    End,
1281}
1282
1283/// Represent movement to a position along a 1-dimensional line.
1284#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1285pub enum MovePosition {
1286    /// Move to the beginning of some range.
1287    ///
1288    /// ## Example: Using `action!`
1289    ///
1290    /// ```
1291    /// use editor_types::prelude::*;
1292    /// use editor_types::{action, Action};
1293    ///
1294    /// // All of these are equivalent:
1295    /// let scroll: Action = Action::Scroll(
1296    ///     ScrollStyle::LinePos(MovePosition::Beginning, 1.into()));
1297    /// assert_eq!(scroll, action!("scroll -s (line-pos -p b -c 1)"));
1298    /// assert_eq!(scroll, action!("scroll -s (line-pos -p beginning -c 1)"));
1299    /// ```
1300    Beginning,
1301
1302    /// Move to the middle of some range.
1303    ///
1304    /// ## Example: Using `action!`
1305    ///
1306    /// ```
1307    /// use editor_types::prelude::*;
1308    /// use editor_types::{action, Action};
1309    ///
1310    /// // All of these are equivalent:
1311    /// let scroll: Action = Action::Scroll(
1312    ///     ScrollStyle::LinePos(MovePosition::Middle, 1.into()));
1313    /// assert_eq!(scroll, action!("scroll -s (line-pos -p m -c 1)"));
1314    /// assert_eq!(scroll, action!("scroll -s (line-pos -p middle -c 1)"));
1315    /// ```
1316    Middle,
1317
1318    /// Move to the end of some range.
1319    ///
1320    /// ## Example: Using `action!`
1321    ///
1322    /// ```
1323    /// use editor_types::prelude::*;
1324    /// use editor_types::{action, Action};
1325    ///
1326    /// // All of these are equivalent:
1327    /// let scroll: Action = Action::Scroll(
1328    ///     ScrollStyle::LinePos(MovePosition::End, 1.into()));
1329    /// assert_eq!(scroll, action!("scroll -s (line-pos -p e -c 1)"));
1330    /// assert_eq!(scroll, action!("scroll -s (line-pos -p end -c 1)"));
1331    /// ```
1332    End,
1333}
1334
1335/// Represents a modification of a previous [MoveDir1D] movement.
1336#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1337pub enum MoveDirMod {
1338    /// Use the same movement previously used.
1339    ///
1340    /// ## Example: Using `action!`
1341    ///
1342    /// ```
1343    /// use editor_types::prelude::*;
1344    /// use editor_types::{action, Action, CommandBarAction};
1345    ///
1346    /// let dir = MoveDirMod::Same;
1347    /// let search: Action = action!("search -d same");
1348    /// assert_eq!(search, Action::Search(dir, Count::Contextual));
1349    /// ```
1350    Same,
1351
1352    /// Use the opposite of the movement previously used.
1353    ///
1354    /// ## Example: Using `action!`
1355    ///
1356    /// ```
1357    /// use editor_types::prelude::*;
1358    /// use editor_types::{action, Action, CommandBarAction};
1359    ///
1360    /// let dir = MoveDirMod::Flip;
1361    /// let search: Action = action!("search -d flip");
1362    /// assert_eq!(search, Action::Search(dir, Count::Contextual));
1363    /// ```
1364    Flip,
1365
1366    /// Ignore whatever value was previously used.
1367    ///
1368    /// ## Example: Using `action!`
1369    ///
1370    /// ```
1371    /// use editor_types::prelude::*;
1372    /// use editor_types::{action, Action, CommandBarAction};
1373    ///
1374    /// let dir = MoveDirMod::Exact(MoveDir1D::Previous);
1375    /// let search: Action = action!("search -d (exact prev)");
1376    /// assert_eq!(search, Action::Search(dir, Count::Contextual));
1377    ///
1378    /// let dir = MoveDirMod::Exact(MoveDir1D::Next);
1379    /// let search: Action = action!("search -d (exact next)");
1380    /// assert_eq!(search, Action::Search(dir, Count::Contextual));
1381    /// ```
1382    Exact(MoveDir1D),
1383}
1384
1385/// This represents a selection of a 2-dimensional axis.
1386#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1387pub enum Axis {
1388    /// The horizontal axis.
1389    ///
1390    /// ## Example: Using `action!`
1391    ///
1392    /// ```
1393    /// use editor_types::prelude::*;
1394    /// use editor_types::{action, Action};
1395    ///
1396    /// let axis = Axis::Horizontal;
1397    /// let scroll: Action = Action::Scroll(ScrollStyle::CursorPos(MovePosition::End, axis));
1398    ///
1399    /// // All of these are equivalent:
1400    /// assert_eq!(scroll, action!("scroll -s (cursor-pos -p end -x horizontal)"));
1401    /// assert_eq!(scroll, action!("scroll -s (cursor-pos -p end -x h)"));
1402    /// assert_eq!(scroll, action!("scroll -s (cursor-pos -p end -x {axis})"));
1403    /// ```
1404    Horizontal,
1405
1406    /// The vertical axis.
1407    ///
1408    /// ## Example: Using `action!`
1409    ///
1410    /// ```
1411    /// use editor_types::prelude::*;
1412    /// use editor_types::{action, Action};
1413    ///
1414    /// let axis = Axis::Vertical;
1415    /// let scroll: Action = Action::Scroll(ScrollStyle::CursorPos(MovePosition::End, axis));
1416    ///
1417    /// // All of these are equivalent:
1418    /// assert_eq!(scroll, action!("scroll -s (cursor-pos -p end -x vertical)"));
1419    /// assert_eq!(scroll, action!("scroll -s (cursor-pos -p end -x v)"));
1420    /// assert_eq!(scroll, action!("scroll -s (cursor-pos -p end -x {axis})"));
1421    /// ```
1422    Vertical,
1423}
1424
1425impl Axis {
1426    /// Rotate a 2-dimensional axis to its opposite.
1427    pub fn rotate(&self) -> Axis {
1428        match self {
1429            Axis::Horizontal => Axis::Vertical,
1430            Axis::Vertical => Axis::Horizontal,
1431        }
1432    }
1433}
1434
1435/// This represents the units used when scrolling.
1436#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1437pub enum ScrollSize {
1438    /// Scroll by number of character cells.
1439    ///
1440    /// ## Example: Using `action!`
1441    ///
1442    /// ```
1443    /// use editor_types::prelude::*;
1444    /// use editor_types::{action, Action};
1445    ///
1446    /// let scroll: Action = action!("scroll -s (dir2d -d up -z cell)");
1447    /// let style = ScrollStyle::Direction2D(MoveDir2D::Up, ScrollSize::Cell, Count::Contextual);
1448    /// assert_eq!(scroll, Action::Scroll(style));
1449    /// ```
1450    Cell,
1451
1452    /// Scroll by [*n*](Count) times half the page size.
1453    ///
1454    /// ## Example: Using `action!`
1455    ///
1456    /// ```
1457    /// use editor_types::prelude::*;
1458    /// use editor_types::{action, Action};
1459    ///
1460    /// let scroll: Action = action!("scroll -s (dir2d -d up -z half-page)");
1461    /// let style = ScrollStyle::Direction2D(MoveDir2D::Up, ScrollSize::HalfPage, Count::Contextual);
1462    /// assert_eq!(scroll, Action::Scroll(style));
1463    /// ```
1464    HalfPage,
1465
1466    /// Scroll by [*n*](Count) times the page size.
1467    ///
1468    /// ## Example: Using `action!`
1469    ///
1470    /// ```
1471    /// use editor_types::prelude::*;
1472    /// use editor_types::{action, Action};
1473    ///
1474    /// let scroll: Action = action!("scroll -s (dir2d -d up -z page)");
1475    /// let style = ScrollStyle::Direction2D(MoveDir2D::Up, ScrollSize::Page, Count::Contextual);
1476    /// assert_eq!(scroll, Action::Scroll(style));
1477    /// ```
1478    Page,
1479}
1480
1481/// This represents the way in which the viewport should be scrolled.
1482#[derive(Clone, Debug, Eq, PartialEq)]
1483pub enum ScrollStyle {
1484    /// Scroll the viewport in [MoveDir2D] direction by [ScrollSize] units, [*n* times](Count).
1485    ///
1486    /// ## Example: Using `action!`
1487    ///
1488    /// ```
1489    /// use editor_types::prelude::*;
1490    /// use editor_types::{action, Action};
1491    ///
1492    /// let scroll: Action = action!("scroll -s (dir2d -d up -z half-page)");
1493    /// let style = ScrollStyle::Direction2D(MoveDir2D::Up, ScrollSize::HalfPage, Count::Contextual);
1494    /// assert_eq!(scroll, Action::Scroll(style));
1495    /// ```
1496    ///
1497    /// See the documentation for [ScrollSize] for how to construct each of its variants with
1498    /// [action].
1499    Direction2D(MoveDir2D, ScrollSize, Count),
1500
1501    /// Scroll the viewport so that the cursor is placed at [MovePosition] relative to [Axis].
1502    ///
1503    /// ## Example: Using `action!`
1504    ///
1505    /// ```
1506    /// use editor_types::prelude::*;
1507    /// use editor_types::{action, Action};
1508    ///
1509    /// let scroll: Action = action!("scroll -s (cursor-pos -p end -x vertical)");
1510    /// let style = ScrollStyle::CursorPos(MovePosition::End, Axis::Vertical);
1511    /// assert_eq!(scroll, Action::Scroll(style));
1512    /// ```
1513    ///
1514    /// See the documentation for [Axis] and [MovePosition] for how to construct each of their
1515    /// variants with [action].
1516    CursorPos(MovePosition, Axis),
1517
1518    /// Scroll the viewport so that the [*n*<sup>th</sup> line](Count) is at [MovePosition] on the screen.
1519    ///
1520    /// ## Example: Using `action!`
1521    ///
1522    /// ```
1523    /// use editor_types::prelude::*;
1524    /// use editor_types::{action, Action};
1525    ///
1526    /// let scroll: Action = action!("scroll -s (line-pos -p end -c 1)");
1527    /// let style = ScrollStyle::LinePos(MovePosition::End, 1.into());
1528    /// assert_eq!(scroll, Action::Scroll(style));
1529    /// ```
1530    ///
1531    /// See the documentation for [MovePosition] for how to construct each of its variants with
1532    /// [action].
1533    LinePos(MovePosition, Count),
1534}
1535
1536/// Place the cursor at a specified position in a visual selection, with the anchor now at the
1537/// opposite end.
1538#[derive(Clone, Debug, Eq, PartialEq)]
1539pub enum SelectionCursorChange {
1540    /// Place the cursor in the first line of the selection, in the first column of the selection.
1541    ///
1542    /// ## Example: Using `action!`
1543    ///
1544    /// ```
1545    /// use editor_types::prelude::*;
1546    /// use editor_types::{action, Action, SelectionAction};
1547    ///
1548    /// let change = SelectionCursorChange::Beginning;
1549    /// let act: Action = action!("selection cursor-set -f beginning");
1550    /// assert_eq!(act, SelectionAction::CursorSet(change).into());
1551    /// ```
1552    Beginning,
1553
1554    /// Place the cursor in the last line of the selection, in the last column of the selection.
1555    ///
1556    /// ## Example: Using `action!`
1557    ///
1558    /// ```
1559    /// use editor_types::prelude::*;
1560    /// use editor_types::{action, Action, SelectionAction};
1561    ///
1562    /// let change = SelectionCursorChange::End;
1563    /// let act: Action = action!("selection cursor-set -f end");
1564    /// assert_eq!(act, SelectionAction::CursorSet(change).into());
1565    /// ```
1566    End,
1567
1568    /// Swap the cursor with the anchor of the selection.
1569    ///
1570    /// ## Example: Using `action!`
1571    ///
1572    /// ```
1573    /// use editor_types::prelude::*;
1574    /// use editor_types::{action, Action, SelectionAction};
1575    ///
1576    /// let change = SelectionCursorChange::SwapAnchor;
1577    /// let act: Action = action!("selection cursor-set -f swap-anchor");
1578    /// assert_eq!(act, SelectionAction::CursorSet(change).into());
1579    /// ```
1580    SwapAnchor,
1581
1582    /// Move the cursor to the other side of the selection.
1583    ///
1584    /// The "other side" of the selection depends on its [shape][TargetShape]:
1585    ///
1586    /// * When the selection is [BlockWise](TargetShape::BlockWise), the
1587    ///   cursor and anchor will stay on their current line, but change
1588    ///   columns to be placed on the opposite side of the selection's block.
1589    /// * When the selection is [LineWise](TargetShape::LineWise), the
1590    ///   other side of the selection is the anchor.
1591    /// * When the selection is [CharWise](TargetShape::CharWise), the
1592    ///   other side of the selection is the anchor.
1593    ///
1594    /// ## Example: Using `action!`
1595    ///
1596    /// ```
1597    /// use editor_types::prelude::*;
1598    /// use editor_types::{action, Action, SelectionAction};
1599    ///
1600    /// let change = SelectionCursorChange::SwapSide;
1601    /// let act: Action = action!("selection cursor-set -f swap-side");
1602    /// assert_eq!(act, SelectionAction::CursorSet(change).into());
1603    /// ```
1604    SwapSide,
1605}
1606
1607/// This represents what UI element is targeted during an Action.
1608#[derive(Clone, Debug, Eq, PartialEq)]
1609pub enum FocusChange {
1610    /// Target the currently focused UI element.
1611    ///
1612    /// ## Example: Using `action!`
1613    ///
1614    /// ```
1615    /// use editor_types::prelude::*;
1616    /// use editor_types::{action, Action, TabAction};
1617    ///
1618    /// let fc = FocusChange::Current;
1619    /// let act: Action = TabAction::Focus(fc).into();
1620    /// assert_eq!(act, action!("tab focus -f current"));
1621    /// ```
1622    Current,
1623
1624    /// Target the [*n*<sup>th</sup> element](Count) from the beginning. The first element is numbered 1.
1625    ///
1626    /// If the specified *n* is greater than the number of elements, and [bool] is `true`, target
1627    /// the last element. Otherwise, do nothing.
1628    ///
1629    /// ## Example: Using `action!`
1630    ///
1631    /// ```
1632    /// use editor_types::prelude::*;
1633    /// use editor_types::{action, Action, TabAction};
1634    ///
1635    /// let fc = FocusChange::Offset(2.into(), false);
1636    /// let act: Action = TabAction::Focus(fc).into();
1637    /// assert_eq!(act, action!("tab focus -f (offset -c 2 -l false)"));
1638    /// ```
1639    Offset(Count, bool),
1640
1641    /// Target the element at [MovePosition] in the element list.
1642    ///
1643    /// ## Example: Using `action!`
1644    ///
1645    /// ```
1646    /// use editor_types::prelude::*;
1647    /// use editor_types::{action, Action, TabAction};
1648    ///
1649    /// // All of these are equivalent:
1650    /// let fc = FocusChange::Position(MovePosition::End);
1651    /// let act: Action = TabAction::Focus(fc).into();
1652    /// assert_eq!(act, action!("tab focus -f (pos -p end)"));
1653    /// assert_eq!(act, action!("tab focus -f (position -p end)"));
1654    /// ```
1655    ///
1656    /// See the documentation for [MovePosition] for how to construct each of its variants with
1657    /// [action].
1658    Position(MovePosition),
1659
1660    /// Target the previously focused element.
1661    ///
1662    /// ## Example: Using `action!`
1663    ///
1664    /// ```
1665    /// use editor_types::prelude::*;
1666    /// use editor_types::{action, Action, TabAction};
1667    ///
1668    /// // All of these are equivalent:
1669    /// let fc = FocusChange::PreviouslyFocused;
1670    /// let act: Action = TabAction::Focus(fc).into();
1671    /// assert_eq!(act, action!("tab focus -f previously-focused"));
1672    /// assert_eq!(act, action!("tab focus -f previous"));
1673    /// assert_eq!(act, action!("tab focus -f prev"));
1674    /// ```
1675    PreviouslyFocused,
1676
1677    /// Target the element [*n* times](Count) away in [MoveDir1D] direction.
1678    ///
1679    /// If moving [*n* times](Count) would go past the first or last element, and [bool] is `true`, wrap
1680    /// around to the other end of the element list and continue from there. Otherwise, do nothing.
1681    ///
1682    /// ## Example: Using `action!`
1683    ///
1684    /// ```
1685    /// use editor_types::prelude::*;
1686    /// use editor_types::{action, Action, TabAction};
1687    ///
1688    /// // All of these are equivalent:
1689    /// let fc = FocusChange::Direction1D(MoveDir1D::Next, 4.into(), true);
1690    /// let act: Action = TabAction::Focus(fc).into();
1691    /// assert_eq!(act, action!("tab focus -f (dir1d -d next -c 4 -w true)"));
1692    /// ```
1693    Direction1D(MoveDir1D, Count, bool),
1694
1695    /// Target the element [*n* times](Count) away in [MoveDir2D] direction.
1696    ///
1697    /// ## Example: Using `action!`
1698    ///
1699    /// ```
1700    /// use editor_types::prelude::*;
1701    /// use editor_types::{action, Action, TabAction};
1702    ///
1703    /// // All of these are equivalent:
1704    /// let fc = FocusChange::Direction2D(MoveDir2D::Up, 3.into());
1705    /// let act: Action = TabAction::Focus(fc).into();
1706    /// assert_eq!(act, action!("tab focus -f (dir2d -d up -c 3)"));
1707    /// ```
1708    Direction2D(MoveDir2D, Count),
1709}
1710
1711/// This represents how to change the size of a window.
1712#[derive(Clone, Debug, Eq, PartialEq)]
1713pub enum SizeChange<I = Count> {
1714    /// Make the window and others along the specified axis the same size.
1715    ///
1716    /// ## Example: Using `action!`
1717    ///
1718    /// ```
1719    /// use editor_types::prelude::*;
1720    /// use editor_types::{action, Action, WindowAction};
1721    ///
1722    /// let size = SizeChange::Equal;
1723    /// let act: Action = WindowAction::Resize(FocusChange::Current, Axis::Vertical, size).into();
1724    ///
1725    /// // All of these are equivalent:
1726    /// assert_eq!(act, action!("window resize -f current -x vertical -z equal"));
1727    /// assert_eq!(act, action!("window resize -f current -x vertical -z eq"));
1728    /// ```
1729    Equal,
1730
1731    /// Make the window exactly a specific size along the axis.
1732    ///
1733    /// ## Example: Using `action!`
1734    ///
1735    /// ```
1736    /// use editor_types::prelude::*;
1737    /// use editor_types::{action, Action, WindowAction};
1738    ///
1739    /// let size = SizeChange::Exact(5.into());
1740    /// let act: Action = WindowAction::Resize(FocusChange::Current, Axis::Vertical, size).into();
1741    /// assert_eq!(act, action!("window resize -f current -x vertical -z (exact 5)"));
1742    /// ```
1743    Exact(I),
1744
1745    /// Decrease the size of the window by a specific amount.
1746    ///
1747    /// ## Example: Using `action!`
1748    ///
1749    /// ```
1750    /// use editor_types::prelude::*;
1751    /// use editor_types::{action, Action, WindowAction};
1752    ///
1753    /// // All of these are equivalent:
1754    /// let size = SizeChange::Decrease(5.into());
1755    /// let act: Action = WindowAction::Resize(FocusChange::Current, Axis::Vertical, size).into();
1756    /// assert_eq!(act, action!("window resize -f current -x vertical -z (decrease 5)"));
1757    /// assert_eq!(act, action!("window resize -f current -x vertical -z (dec 5)"));
1758    /// ```
1759    Decrease(I),
1760
1761    /// Increase the size of the window by a specific amount.
1762    ///
1763    /// ## Example: Using `action!`
1764    ///
1765    /// ```
1766    /// use editor_types::prelude::*;
1767    /// use editor_types::{action, Action, WindowAction};
1768    ///
1769    /// // All of these are equivalent:
1770    /// let size = SizeChange::Increase(5.into());
1771    /// let act: Action = WindowAction::Resize(FocusChange::Current, Axis::Vertical, size).into();
1772    /// assert_eq!(act, action!("window resize -f current -x vertical -z (increase 5)"));
1773    /// assert_eq!(act, action!("window resize -f current -x vertical -z (inc 5)"));
1774    /// ```
1775    Increase(I),
1776}
1777
1778/// This represents how to change the indentation of a range.
1779#[derive(Clone, Debug, Eq, PartialEq)]
1780pub enum IndentChange<I = Count> {
1781    /// Automatically determine indentation level.
1782    Auto,
1783
1784    /// Decrease the indentation level of indentation.
1785    Decrease(I),
1786
1787    /// Increase the indentation level of indentation.
1788    Increase(I),
1789}
1790
1791/// This represents how to change a number in text.
1792#[derive(Clone, Debug, Eq, PartialEq)]
1793pub enum NumberChange {
1794    /// Decrease the first number in the targeted text by [*n*](Count).
1795    Decrease(Count),
1796
1797    /// Increase the first number in the targeted text by [*n*](Count).
1798    Increase(Count),
1799}
1800
1801/// Targets for [Action::KeywordLookup].
1802#[derive(Clone, Debug, Eq, PartialEq)]
1803pub enum KeywordTarget {
1804    /// Lookup the [word][WordStyle] surrounding the cursor.
1805    ///
1806    /// ## Example: Using `action!`
1807    ///
1808    /// ```
1809    /// use editor_types::prelude::*;
1810    /// use editor_types::{action, Action};
1811    ///
1812    /// let word = WordStyle::Little;
1813    /// let target = KeywordTarget::Word(word.clone());
1814    /// let act: Action = Action::KeywordLookup(target.clone());
1815    ///
1816    /// // All of these are equivalent:
1817    /// assert_eq!(act, action!("keyword-lookup -t {target}"));
1818    /// assert_eq!(act, action!("keyword-lookup -t (word little)"));
1819    /// assert_eq!(act, action!("keyword-lookup -t (word {})", word.clone()));
1820    /// assert_eq!(act, action!("keyword-lookup -t {word}", word.clone()));
1821    /// ```
1822    Word(WordStyle),
1823
1824    /// Lookup the currently selected text.
1825    ///
1826    /// ## Example: Using `action!`
1827    ///
1828    /// ```
1829    /// use editor_types::prelude::*;
1830    /// use editor_types::{action, Action};
1831    ///
1832    /// let target = KeywordTarget::Selection;
1833    /// let act: Action = Action::KeywordLookup(target.clone());
1834    ///
1835    /// // All of these are equivalent:
1836    /// assert_eq!(act, action!("keyword-lookup -t {target}"));
1837    /// assert_eq!(act, action!("keyword-lookup -t selection"));
1838    /// ```
1839    Selection,
1840}
1841
1842impl From<WordStyle> for KeywordTarget {
1843    fn from(style: WordStyle) -> KeywordTarget {
1844        KeywordTarget::Word(style)
1845    }
1846}
1847
1848/// Targets for [WindowAction::Open] and [WindowAction::Switch].
1849///
1850/// [WindowAction::Open]: crate::WindowAction::Open
1851/// [WindowAction::Switch]: crate::WindowAction::Switch
1852#[derive(Clone, Debug, Eq, PartialEq)]
1853pub enum OpenTarget<W: ApplicationWindowId> {
1854    /// An alternate window. This is usually the previous window.
1855    ///
1856    /// ## Example: Using `action!`
1857    ///
1858    /// ```
1859    /// use editor_types::prelude::*;
1860    /// use editor_types::{action, Action, WindowAction};
1861    ///
1862    /// let target = OpenTarget::Alternate;
1863    /// let switch: Action = WindowAction::Switch(target).into();
1864    /// assert_eq!(switch, action!("window switch -t alternate"));
1865    /// ```
1866    Alternate,
1867
1868    /// An application-specific [identifier][ApplicationWindowId] to switch to.
1869    Application(W),
1870
1871    /// Use the current window as the target.
1872    ///
1873    /// ## Example: Using `action!`
1874    ///
1875    /// ```
1876    /// use editor_types::prelude::*;
1877    /// use editor_types::{action, Action, WindowAction};
1878    ///
1879    /// let target = OpenTarget::Current;
1880    /// let switch: Action = WindowAction::Switch(target).into();
1881    /// assert_eq!(switch, action!("window switch -t current"));
1882    /// ```
1883    Current,
1884
1885    /// Use the [word](WordStyle) under the cursor as a target name.
1886    ///
1887    /// ## Example: Using `action!`
1888    ///
1889    /// ```
1890    /// use editor_types::prelude::*;
1891    /// use editor_types::{action, Action, WindowAction};
1892    ///
1893    /// let target = OpenTarget::Cursor(WordStyle::FileName);
1894    /// let switch: Action = WindowAction::Switch(target).into();
1895    /// assert_eq!(switch, action!("window switch -t (cursor -s filename)"));
1896    /// ```
1897    ///
1898    /// See the documentation for [WordStyle] for how to construct each of its variants with
1899    /// [action].
1900    Cursor(WordStyle),
1901
1902    /// An absolute position in a list of targets.
1903    ///
1904    /// ## Example: Using `action!`
1905    ///
1906    /// ```
1907    /// use editor_types::prelude::*;
1908    /// use editor_types::{action, Action, WindowAction};
1909    ///
1910    /// let target = OpenTarget::List(2.into());
1911    /// let switch: Action = WindowAction::Switch(target).into();
1912    /// assert_eq!(switch, action!("window switch -t (list -c 2)"));
1913    /// ```
1914    List(Count),
1915
1916    /// A named target (e.g., a filename to open).
1917    ///
1918    /// ## Example: Using `action!`
1919    ///
1920    /// ```
1921    /// use editor_types::prelude::*;
1922    /// use editor_types::{action, Action, WindowAction};
1923    ///
1924    /// let target = OpenTarget::Name("foo bar".into());
1925    /// let switch: Action = WindowAction::Switch(target).into();
1926    /// assert_eq!(switch, action!(r#"window switch -t (name -i "foo bar")"#));
1927    /// ```
1928    Name(String),
1929
1930    /// A window offset from the current one.
1931    ///
1932    /// ## Example: Using `action!`
1933    ///
1934    /// ```
1935    /// use editor_types::prelude::*;
1936    /// use editor_types::{action, Action, WindowAction};
1937    ///
1938    /// let target = OpenTarget::Offset(MoveDir1D::Next, 5.into());
1939    /// let switch: Action = WindowAction::Switch(target).into();
1940    /// assert_eq!(switch, action!("window switch -t (offset -d next -c 5)"));
1941    /// ```
1942    Offset(MoveDir1D, Count),
1943
1944    /// Use the selected text as a target name.
1945    ///
1946    /// ## Example: Using `action!`
1947    ///
1948    /// ```
1949    /// use editor_types::prelude::*;
1950    /// use editor_types::{action, Action, WindowAction};
1951    ///
1952    /// let target = OpenTarget::Selection;
1953    /// let switch: Action = WindowAction::Switch(target).into();
1954    /// assert_eq!(switch, action!("window switch -t selection"));
1955    /// ```
1956    Selection,
1957
1958    /// A default window to open when no target has been specified.
1959    ///
1960    /// ## Example: Using `action!`
1961    ///
1962    /// ```
1963    /// use editor_types::prelude::*;
1964    /// use editor_types::{action, Action, WindowAction};
1965    ///
1966    /// let target = OpenTarget::Unnamed;
1967    /// let switch: Action = WindowAction::Switch(target).into();
1968    /// assert_eq!(switch, action!("window switch -t unnamed"));
1969    /// ```
1970    Unnamed,
1971}
1972
1973/// This represents what tabs are targeted by a tab command.
1974#[derive(Clone, Debug, Eq, PartialEq)]
1975pub enum TabTarget {
1976    /// Close the tab targeted by FocusChange.
1977    ///
1978    /// ## Example: Using `action!`
1979    ///
1980    /// ```
1981    /// use editor_types::prelude::*;
1982    /// use editor_types::{action, Action, TabAction};
1983    ///
1984    /// let fc = TabTarget::Single(FocusChange::Current);
1985    /// let flags = CloseFlags::NONE;
1986    /// let act: Action = TabAction::Close(fc, flags).into();
1987    /// assert_eq!(act, action!("tab close -t (single current) -F none"));
1988    /// ```
1989    Single(FocusChange),
1990
1991    /// Close all tab *except* for the one targeted by FocusChange.
1992    ///
1993    /// ## Example: Using `action!`
1994    ///
1995    /// ```
1996    /// use editor_types::prelude::*;
1997    /// use editor_types::{action, Action, TabAction};
1998    ///
1999    /// let fc = TabTarget::AllBut(FocusChange::Current);
2000    /// let flags = CloseFlags::NONE;
2001    /// let act: Action = TabAction::Close(fc, flags).into();
2002    /// assert_eq!(act, action!("tab close -t (all-but current) -F none"));
2003    /// ```
2004    AllBut(FocusChange),
2005
2006    /// Close all tabs.
2007    ///
2008    /// ## Example: Using `action!`
2009    ///
2010    /// ```
2011    /// use editor_types::prelude::*;
2012    /// use editor_types::{action, Action, TabAction};
2013    ///
2014    /// let fc = TabTarget::All;
2015    /// let flags = CloseFlags::NONE;
2016    /// let act: Action = TabAction::Close(fc, flags).into();
2017    /// assert_eq!(act, action!("tab close -t all -F none"));
2018    /// ```
2019    All,
2020}
2021
2022/// This represents what windows are targeted by a window command.
2023#[derive(Clone, Debug, Eq, PartialEq)]
2024pub enum WindowTarget {
2025    /// Close the window targeted by [FocusChange].
2026    ///
2027    /// ## Example: Using `action!`
2028    ///
2029    /// ```
2030    /// use editor_types::prelude::*;
2031    /// use editor_types::{action, Action, WindowAction};
2032    ///
2033    /// let fc = WindowTarget::Single(FocusChange::Current);
2034    /// let flags = CloseFlags::NONE;
2035    /// let act: Action = WindowAction::Close(fc, flags).into();
2036    /// assert_eq!(act, action!("window close -t (single current) -F none"));
2037    /// ```
2038    Single(FocusChange),
2039
2040    /// Close all windows *except* for the one targeted by [FocusChange].
2041    ///
2042    /// ## Example: Using `action!`
2043    ///
2044    /// ```
2045    /// use editor_types::prelude::*;
2046    /// use editor_types::{action, Action, WindowAction};
2047    ///
2048    /// let fc = WindowTarget::AllBut(FocusChange::Current);
2049    /// let flags = CloseFlags::NONE;
2050    /// let act: Action = WindowAction::Close(fc, flags).into();
2051    /// assert_eq!(act, action!("window close -t (all-but current) -F none"));
2052    /// ```
2053    AllBut(FocusChange),
2054
2055    /// Close all windows.
2056    ///
2057    /// ## Example: Using `action!`
2058    ///
2059    /// ```
2060    /// use editor_types::prelude::*;
2061    /// use editor_types::{action, Action, WindowAction};
2062    ///
2063    /// let fc = WindowTarget::All;
2064    /// let flags = CloseFlags::NONE;
2065    /// let act: Action = WindowAction::Close(fc, flags).into();
2066    /// assert_eq!(act, action!("window close -t all -F none"));
2067    /// ```
2068    All,
2069}
2070
2071/// Target cursors in a cursor group.
2072#[derive(Clone, Debug, Eq, PartialEq)]
2073pub enum CursorCloseTarget {
2074    /// Target the cursor group's leader.
2075    ///
2076    /// ## Example: Using `action!`
2077    ///
2078    /// ```
2079    /// use editor_types::prelude::*;
2080    /// use editor_types::{action, Action, CursorAction};
2081    ///
2082    /// let close: Action = action!("cursor close -t leader");
2083    /// assert_eq!(close, CursorAction::Close(CursorCloseTarget::Leader).into());
2084    /// ```
2085    Leader,
2086
2087    /// Target the cursor group's followers.
2088    ///
2089    /// ## Example: Using `action!`
2090    ///
2091    /// ```
2092    /// use editor_types::prelude::*;
2093    /// use editor_types::{action, Action, CursorAction};
2094    ///
2095    /// let close: Action = action!("cursor close -t followers");
2096    /// assert_eq!(close, CursorAction::Close(CursorCloseTarget::Followers).into());
2097    /// ```
2098    Followers,
2099}
2100
2101/// Ways to combine a newer cursor group with an already existing one.
2102#[derive(Clone, Debug, Eq, PartialEq)]
2103pub enum CursorGroupCombineStyle {
2104    /// Use all of the selections from both groups.
2105    ///
2106    /// ## Example: Using `action!`
2107    ///
2108    /// ```
2109    /// use editor_types::prelude::*;
2110    /// use editor_types::{action, Action, CursorAction};
2111    ///
2112    /// let combine = CursorGroupCombineStyle::Append;
2113    /// let restore: Action = action!("cursor restore -s append");
2114    /// assert_eq!(restore, CursorAction::Restore(combine).into());
2115    /// ```
2116    Append,
2117
2118    /// Merge each member with the matching member in the other group.
2119    ///
2120    /// This fails if the groups have a different number of members.
2121    ///
2122    /// ## Example: Using `action!`
2123    ///
2124    /// ```
2125    /// use editor_types::prelude::*;
2126    /// use editor_types::{action, Action, CursorAction};
2127    ///
2128    /// let combine = CursorGroupCombineStyle::Merge(CursorMergeStyle::Union);
2129    /// let restore: Action = action!("cursor restore -s (merge union)");
2130    /// assert_eq!(restore, CursorAction::Restore(combine).into());
2131    /// ```
2132    ///
2133    /// See the documentation for [CursorMergeStyle] for how to construct each of its
2134    /// variants with [action].
2135    Merge(CursorMergeStyle),
2136
2137    /// Use only the selections in the newer group.
2138    ///
2139    /// ## Example: Using `action!`
2140    ///
2141    /// ```
2142    /// use editor_types::prelude::*;
2143    /// use editor_types::{action, Action, CursorAction};
2144    ///
2145    /// let combine = CursorGroupCombineStyle::Replace;
2146    /// let restore: Action = action!("cursor restore -s replace");
2147    /// assert_eq!(restore, CursorAction::Restore(combine).into());
2148    /// ```
2149    Replace,
2150}
2151
2152impl From<CursorMergeStyle> for CursorGroupCombineStyle {
2153    fn from(style: CursorMergeStyle) -> Self {
2154        CursorGroupCombineStyle::Merge(style)
2155    }
2156}
2157
2158/// Ways to combine two selections.
2159#[derive(Clone, Debug, Eq, PartialEq)]
2160pub enum CursorMergeStyle {
2161    /// Merge the two selections to form one long selection.
2162    ///
2163    /// ## Example: Using `action!`
2164    ///
2165    /// ```
2166    /// use editor_types::prelude::*;
2167    /// use editor_types::{action, Action, CursorAction};
2168    ///
2169    /// let merge = CursorMergeStyle::Union;
2170    /// let save: Action = action!("cursor save -s (merge union)");
2171    /// assert_eq!(save, CursorAction::Save(CursorGroupCombineStyle::Merge(merge)).into());
2172    /// ```
2173    Union,
2174
2175    /// Use the intersecting region of the two selections.
2176    ///
2177    /// ## Example: Using `action!`
2178    ///
2179    /// ```
2180    /// use editor_types::prelude::*;
2181    /// use editor_types::{action, Action, CursorAction};
2182    ///
2183    /// let merge = CursorMergeStyle::Intersect;
2184    /// let save: Action = action!("cursor save -s (merge intersect)");
2185    /// assert_eq!(save, CursorAction::Save(CursorGroupCombineStyle::Merge(merge)).into());
2186    /// ```
2187    Intersect,
2188
2189    /// Select the one where the cursor is furthest in [MoveDir1D] direction.
2190    ///
2191    /// ## Example: Using `action!`
2192    ///
2193    /// ```
2194    /// use editor_types::prelude::*;
2195    /// use editor_types::{action, Action, CursorAction};
2196    ///
2197    /// let merge = CursorMergeStyle::SelectCursor(MoveDir1D::Previous);
2198    /// let save: Action = action!("cursor save -s (merge select-cursor -d prev)");
2199    /// assert_eq!(save, CursorAction::Save(CursorGroupCombineStyle::Merge(merge)).into());
2200    /// ```
2201    SelectCursor(MoveDir1D),
2202
2203    /// Select the shortest selection.
2204    ///
2205    /// ## Example: Using `action!`
2206    ///
2207    /// ```
2208    /// use editor_types::prelude::*;
2209    /// use editor_types::{action, Action, CursorAction};
2210    ///
2211    /// let merge = CursorMergeStyle::SelectShort;
2212    /// let save: Action = action!("cursor save -s (merge select-short)");
2213    /// assert_eq!(save, CursorAction::Save(CursorGroupCombineStyle::Merge(merge)).into());
2214    /// ```
2215    SelectShort,
2216
2217    /// Select the longest selection.
2218    ///
2219    /// ## Example: Using `action!`
2220    ///
2221    /// ```
2222    /// use editor_types::prelude::*;
2223    /// use editor_types::{action, Action, CursorAction};
2224    ///
2225    /// let merge = CursorMergeStyle::SelectLong;
2226    /// let save: Action = action!("cursor save -s (merge select-long)");
2227    /// assert_eq!(save, CursorAction::Save(CursorGroupCombineStyle::Merge(merge)).into());
2228    /// ```
2229    SelectLong,
2230}
2231
2232/// This represents how to determine what count argument should be applied to an action.
2233#[derive(Clone, Debug, Eq, PartialEq)]
2234pub enum Count {
2235    /// Use the count provided by the user, or 1 if one was not given.
2236    Contextual,
2237    /// Use the count provided by the user minus 1, or 0 if one was not given.
2238    MinusOne,
2239    /// Ignore the count provided by the user, and use the exact amount specified here.
2240    Exact(usize),
2241}
2242
2243impl From<usize> for Count {
2244    fn from(n: usize) -> Self {
2245        Count::Exact(n)
2246    }
2247}
2248
2249/// Saved cursor positions.
2250#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2251pub enum Mark {
2252    /// The position of the cursor in the current buffer when it last exited.
2253    ///
2254    /// For example, `'"` in Vim.
2255    ///
2256    /// ## Example: Using `action!`
2257    ///
2258    /// ```
2259    /// use editor_types::prelude::*;
2260    /// use editor_types::{action, Action, EditorAction};
2261    ///
2262    /// let act: Action = action!("mark -m (exact buffer-last-exited)");
2263    /// let exp: Action = EditorAction::Mark(Mark::BufferLastExited.into()).into();
2264    /// assert_eq!(act, exp);
2265    /// ```
2266    BufferLastExited,
2267
2268    /// A user-named position in the current buffer.
2269    ///
2270    /// For example, `'[a-z]` in Vim.
2271    ///
2272    /// ## Example: Using `action!`
2273    ///
2274    /// ```
2275    /// use editor_types::prelude::*;
2276    /// use editor_types::{action, Action, EditorAction};
2277    ///
2278    /// let act: Action = action!("mark -m (exact buffer-named 'c')");
2279    /// let exp: Action = EditorAction::Mark(Mark::BufferNamed('c').into()).into();
2280    /// assert_eq!(act, exp);
2281    /// ```
2282    BufferNamed(char),
2283
2284    /// The position of the current when the application was previously exited.
2285    ///
2286    /// Index 0 is the cursor position the last time the application exited, 1 the position the
2287    /// second most recent exit, and so on.
2288    ///
2289    /// For example, `'[0-9]` in Vim.
2290    ///
2291    /// ## Example: Using `action!`
2292    ///
2293    /// ```
2294    /// use editor_types::prelude::*;
2295    /// use editor_types::{action, Action, EditorAction};
2296    ///
2297    /// let act: Action = action!("mark -m (exact global-last-exited 1)");
2298    /// let exp: Action = EditorAction::Mark(Mark::GlobalLastExited(1).into()).into();
2299    /// assert_eq!(act, exp);
2300    /// ```
2301    GlobalLastExited(usize),
2302
2303    /// A global, user-named position in some buffer known to the application.
2304    ///
2305    /// For example, `'[A-Z]` in Vim.
2306    ///
2307    /// ## Example: Using `action!`
2308    ///
2309    /// ```
2310    /// use editor_types::prelude::*;
2311    /// use editor_types::{action, Action, EditorAction};
2312    ///
2313    /// let act: Action = action!("mark -m (exact global-named 'C')");
2314    /// let exp: Action = EditorAction::Mark(Mark::GlobalNamed('C').into()).into();
2315    /// assert_eq!(act, exp);
2316    /// ```
2317    GlobalNamed(char),
2318
2319    /// The cursor position where the last change was made.
2320    ///
2321    /// For example, `'.` in Vim.
2322    ///
2323    /// ## Example: Using `action!`
2324    ///
2325    /// ```
2326    /// use editor_types::prelude::*;
2327    /// use editor_types::{action, Action, EditorAction};
2328    ///
2329    /// let act: Action = action!("mark -m (exact last-changed)");
2330    /// let exp: Action = EditorAction::Mark(Mark::LastChanged.into()).into();
2331    /// assert_eq!(act, exp);
2332    /// ```
2333    LastChanged,
2334
2335    /// The cursor position where the last text was inserted.
2336    ///
2337    /// For example, `'^` in Vim.
2338    ///
2339    /// ## Example: Using `action!`
2340    ///
2341    /// ```
2342    /// use editor_types::prelude::*;
2343    /// use editor_types::{action, Action, EditorAction};
2344    ///
2345    /// let act: Action = action!("mark -m (exact last-inserted)");
2346    /// let exp: Action = EditorAction::Mark(Mark::LastInserted.into()).into();
2347    /// assert_eq!(act, exp);
2348    /// ```
2349    LastInserted,
2350
2351    /// The cursor position before the latest jump.
2352    ///
2353    /// For example, `''` and `` '` `` in Vim.
2354    ///
2355    /// ## Example: Using `action!`
2356    ///
2357    /// ```
2358    /// use editor_types::prelude::*;
2359    /// use editor_types::{action, Action, EditorAction};
2360    ///
2361    /// let act: Action = action!("mark -m (exact last-jump)");
2362    /// let exp: Action = EditorAction::Mark(Mark::LastJump.into()).into();
2363    /// assert_eq!(act, exp);
2364    /// ```
2365    LastJump,
2366
2367    /// The position of the beginning of the last text selection.
2368    ///
2369    /// For example, `'<` in Vim.
2370    ///
2371    /// ## Example: Using `action!`
2372    ///
2373    /// ```
2374    /// use editor_types::prelude::*;
2375    /// use editor_types::{action, Action, EditorAction};
2376    ///
2377    /// let act: Action = action!("mark -m (exact visual-begin)");
2378    /// let exp: Action = EditorAction::Mark(Mark::VisualBegin.into()).into();
2379    /// assert_eq!(act, exp);
2380    /// ```
2381    VisualBegin,
2382
2383    /// The position of the end of the last text selection.
2384    ///
2385    /// For example, `'>` in Vim.
2386    ///
2387    /// ## Example: Using `action!`
2388    ///
2389    /// ```
2390    /// use editor_types::prelude::*;
2391    /// use editor_types::{action, Action, EditorAction};
2392    ///
2393    /// let act: Action = action!("mark -m (exact visual-end)");
2394    /// let exp: Action = EditorAction::Mark(Mark::VisualEnd.into()).into();
2395    /// assert_eq!(act, exp);
2396    /// ```
2397    VisualEnd,
2398
2399    /// The position of the beginning of the last yanked text.
2400    ///
2401    /// For example, `'[` in Vim.
2402    ///
2403    /// ## Example: Using `action!`
2404    ///
2405    /// ```
2406    /// use editor_types::prelude::*;
2407    /// use editor_types::{action, Action, EditorAction};
2408    ///
2409    /// let act: Action = action!("mark -m (exact last-yanked-begin)");
2410    /// let exp: Action = EditorAction::Mark(Mark::LastYankedBegin.into()).into();
2411    /// assert_eq!(act, exp);
2412    /// ```
2413    LastYankedBegin,
2414
2415    /// The position of the end of the last yanked text.
2416    ///
2417    /// For example, `']` in Vim.
2418    ///
2419    /// ## Example: Using `action!`
2420    ///
2421    /// ```
2422    /// use editor_types::prelude::*;
2423    /// use editor_types::{action, Action, EditorAction};
2424    ///
2425    /// let act: Action = action!("mark -m (exact last-yanked-end)");
2426    /// let exp: Action = EditorAction::Mark(Mark::LastYankedEnd.into()).into();
2427    /// assert_eq!(act, exp);
2428    /// ```
2429    LastYankedEnd,
2430}
2431
2432impl Mark {
2433    /// Indicates whether this is a global mark.
2434    pub fn is_global(&self) -> bool {
2435        match self {
2436            Mark::GlobalNamed(_) => true,
2437            Mark::GlobalLastExited(_) => true,
2438
2439            Mark::BufferLastExited => false,
2440            Mark::BufferNamed(_) => false,
2441            Mark::LastChanged => false,
2442            Mark::LastInserted => false,
2443            Mark::LastJump => false,
2444            Mark::VisualBegin => false,
2445            Mark::VisualEnd => false,
2446            Mark::LastYankedBegin => false,
2447            Mark::LastYankedEnd => false,
2448        }
2449    }
2450}
2451
2452/// A value that may not be known now, but is present in the context.
2453#[derive(Clone, Debug, Default, Eq, PartialEq)]
2454pub enum Specifier<T> {
2455    /// Look for a value of `T` in the [EditContext].
2456    #[default]
2457    Contextual,
2458
2459    /// Use the value of `T` provided here.
2460    Exact(T),
2461}
2462
2463impl<T> From<T> for Specifier<T> {
2464    fn from(v: T) -> Self {
2465        Specifier::Exact(v)
2466    }
2467}
2468
2469bitflags! {
2470    /// These flags are used to specify the behaviour while writing a window.
2471    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
2472    pub struct WriteFlags: u32 {
2473        /// No flags set.
2474        ///
2475        /// ## Example: Using `action!`
2476        ///
2477        /// ```
2478        /// use editor_types::prelude::*;
2479        /// use editor_types::{action, Action, WindowAction};
2480        ///
2481        /// let target = WindowTarget::All;
2482        /// let flags = WriteFlags::NONE;
2483        /// let act: Action = WindowAction::Write(target, None, flags).into();
2484        /// assert_eq!(act, action!("window write -t all -F none"));
2485        /// ```
2486        const NONE = 0b00000000;
2487
2488        /// Ignore any issues during closing.
2489        ///
2490        /// ## Example: Using `action!`
2491        ///
2492        /// ```
2493        /// use editor_types::prelude::*;
2494        /// use editor_types::{action, Action, WindowAction};
2495        ///
2496        /// let target = WindowTarget::All;
2497        /// let flags = WriteFlags::FORCE;
2498        /// let act: Action = WindowAction::Write(target, None, flags).into();
2499        /// assert_eq!(act, action!("window write -t all -F force"));
2500        /// ```
2501        const FORCE = 0b00000001;
2502    }
2503}
2504
2505bitflags! {
2506    /// These flags are used to specify the behaviour while opening a window.
2507    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
2508    pub struct OpenFlags: u32 {
2509        /// No flags set.
2510        const NONE = 0b00000000;
2511
2512        /// Try to ignore any issues during opening.
2513        const FORCE = 0b00000001;
2514
2515        /// Attemp to create the target content if it doesn't already exist.
2516        const CREATE = 0b00000010;
2517    }
2518}
2519
2520bitflags! {
2521    /// These flags are used to specify the behaviour while closing a window.
2522    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
2523    pub struct CloseFlags: u32 {
2524        /// No flags set.
2525        ///
2526        /// ## Example: Using `action!`
2527        ///
2528        /// ```
2529        /// use editor_types::prelude::*;
2530        /// use editor_types::{action, Action, TabAction};
2531        ///
2532        /// let fc = TabTarget::Single(FocusChange::Current);
2533        /// let flags = CloseFlags::NONE;
2534        /// let act: Action = TabAction::Close(fc, flags).into();
2535        /// assert_eq!(act, action!("tab close -t (single current) -F none"));
2536        /// ```
2537        const NONE = 0b00000000;
2538
2539        /// Ignore any issues during closing.
2540        ///
2541        /// ## Example: Using `action!`
2542        ///
2543        /// ```
2544        /// use editor_types::prelude::*;
2545        /// use editor_types::{action, Action, TabAction};
2546        ///
2547        /// let fc = TabTarget::Single(FocusChange::Current);
2548        /// let flags = CloseFlags::FORCE;
2549        /// let act: Action = TabAction::Close(fc, flags).into();
2550        /// assert_eq!(act, action!("tab close -t (single current) -F force"));
2551        /// ```
2552        const FORCE = 0b00000001;
2553
2554        /// Write while closing.
2555        ///
2556        /// ## Example: Using `action!`
2557        ///
2558        /// ```
2559        /// use editor_types::prelude::*;
2560        /// use editor_types::{action, Action, TabAction};
2561        ///
2562        /// let fc = TabTarget::Single(FocusChange::Current);
2563        /// let flags = CloseFlags::WRITE;
2564        /// let act: Action = TabAction::Close(fc, flags).into();
2565        /// assert_eq!(act, action!("tab close -t (single current) -F write"));
2566        /// ```
2567        const WRITE = 0b00000010;
2568
2569        /// Quit if this is the last window.
2570        ///
2571        /// ## Example: Using `action!`
2572        ///
2573        /// ```
2574        /// use editor_types::prelude::*;
2575        /// use editor_types::{action, Action, TabAction};
2576        ///
2577        /// let fc = TabTarget::Single(FocusChange::Current);
2578        /// let flags = CloseFlags::QUIT;
2579        /// let act: Action = TabAction::Close(fc, flags).into();
2580        /// assert_eq!(act, action!("tab close -t (single current) -F quit"));
2581        /// ```
2582        const QUIT  = 0b00000100;
2583
2584        /// Write out the window's contents and quit.
2585        const WQ = CloseFlags::WRITE.bits() | CloseFlags::QUIT.bits();
2586
2587        /// Force quit the window.
2588        const FQ = CloseFlags::FORCE.bits() | CloseFlags::QUIT.bits();
2589    }
2590}
2591
2592/// Different ways to expand or trim selections.
2593#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2594#[non_exhaustive]
2595pub enum SelectionBoundary {
2596    /// A selection that starts at the beginning of a line and ends on a newline.
2597    ///
2598    /// ## Example: Using `action!`
2599    ///
2600    /// ```
2601    /// use editor_types::prelude::*;
2602    /// use editor_types::{action, Action, SelectionAction};
2603    ///
2604    /// let style = SelectionBoundary::Line;
2605    /// let split: Action = action!("selection trim -b line");
2606    /// assert_eq!(split, SelectionAction::Trim(style, TargetShapeFilter::ALL).into());
2607    /// ```
2608    Line,
2609
2610    /// A selection that starts on a non-whitespace character and ends on a non-whitespace
2611    /// character.
2612    ///
2613    /// ## Example: Using `action!`
2614    ///
2615    /// ```
2616    /// use editor_types::prelude::*;
2617    /// use editor_types::{action, Action, SelectionAction};
2618    ///
2619    /// let style = SelectionBoundary::NonWhitespace;
2620    /// let act: Action = SelectionAction::Expand(style, TargetShapeFilter::ALL).into();
2621    ///
2622    /// assert_eq!(action!("selection expand -b non-whitespace"), act);
2623    /// assert_eq!(action!("selection expand -b non-ws"), act);
2624    /// ```
2625    NonWhitespace,
2626}
2627
2628impl BoundaryTest for SelectionBoundary {
2629    fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool {
2630        match self {
2631            SelectionBoundary::Line => {
2632                if let Some(before) = ctx.before {
2633                    return before == '\n';
2634                } else {
2635                    return true;
2636                }
2637            },
2638            SelectionBoundary::NonWhitespace => {
2639                return !is_space_char(ctx.current);
2640            },
2641        }
2642    }
2643
2644    fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool {
2645        match self {
2646            SelectionBoundary::Line => ctx.current == '\n' || ctx.after.is_none(),
2647            SelectionBoundary::NonWhitespace => {
2648                return !is_space_char(ctx.current);
2649            },
2650        }
2651    }
2652}
2653
2654/// Different ways to split existing selections into new ones.
2655#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2656#[non_exhaustive]
2657pub enum SelectionSplitStyle {
2658    /// Split a selection into two [TargetShape::CharWise] selections, one at the current cursor
2659    /// position, and the other at the anchor.
2660    ///
2661    /// ## Example: Using `action!`
2662    ///
2663    /// ```
2664    /// use editor_types::prelude::*;
2665    /// use editor_types::{action, Action, SelectionAction};
2666    ///
2667    /// let style = SelectionSplitStyle::Anchor;
2668    /// let split: Action = action!("selection split -s anchor");
2669    /// assert_eq!(split, SelectionAction::Split(style, TargetShapeFilter::ALL).into());
2670    /// ```
2671    Anchor,
2672
2673    /// Split a selection at each line boundary it contains.
2674    ///
2675    /// ## Example: Using `action!`
2676    ///
2677    /// ```
2678    /// use editor_types::prelude::*;
2679    /// use editor_types::{action, Action, SelectionAction};
2680    ///
2681    /// let style = SelectionSplitStyle::Lines;
2682    /// let split: Action = action!("selection split -s lines");
2683    /// assert_eq!(split, SelectionAction::Split(style, TargetShapeFilter::ALL).into());
2684    /// ```
2685    Lines,
2686
2687    /// Split a selection into [TargetShape::CharWise] parts based on the regular expression
2688    /// stored in the register for [CommandType::Search].
2689    ///
2690    /// ## Example: Using `action!`
2691    ///
2692    /// ```
2693    /// use editor_types::prelude::*;
2694    /// use editor_types::{action, Action, SelectionAction};
2695    ///
2696    /// let style = SelectionSplitStyle::Regex(MatchAction::Keep);
2697    /// let split: Action = action!("selection split -s (regex keep)");
2698    /// assert_eq!(split, SelectionAction::Split(style, TargetShapeFilter::ALL).into());
2699    ///
2700    /// let style = SelectionSplitStyle::Regex(MatchAction::Drop);
2701    /// let split: Action = action!("selection split -s (regex drop)");
2702    /// assert_eq!(split, SelectionAction::Split(style, TargetShapeFilter::ALL).into());
2703    /// ```
2704    Regex(MatchAction),
2705}
2706
2707/// Different ways to change the boundaries of a visual selection.
2708#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2709#[non_exhaustive]
2710pub enum SelectionResizeStyle {
2711    /// Extend (or possibly shrink) the selection by moving the cursor.
2712    ///
2713    /// When extending with [EditTarget::Range], this may also move the anchor to fully encompass
2714    /// the [RangeType].
2715    ///
2716    /// ## Example: Using `action!`
2717    ///
2718    /// ```
2719    /// use editor_types::prelude::*;
2720    /// use editor_types::{action, Action, SelectionAction};
2721    ///
2722    /// let style = SelectionResizeStyle::Extend;
2723    /// let target = EditTarget::CurrentPosition;
2724    /// let act: Action = SelectionAction::Resize(style, target.clone()).into();
2725    /// assert_eq!(act, action!("selection resize -s extend -t {target}"));
2726    /// ```
2727    Extend,
2728
2729    /// Interpret the [EditTarget] as the bounds of a text object, and select it.
2730    ///
2731    /// ## Example: Using `action!`
2732    ///
2733    /// ```
2734    /// use editor_types::prelude::*;
2735    /// use editor_types::{action, Action, SelectionAction};
2736    ///
2737    /// let style = SelectionResizeStyle::Object;
2738    /// let target = EditTarget::CurrentPosition;
2739    /// let act: Action = SelectionAction::Resize(style, target.clone()).into();
2740    /// assert_eq!(act, action!("selection resize -s object -t {target}"));
2741    /// ```
2742    Object,
2743
2744    /// Move the anchor to the current cursor position and create a new selection from there.
2745    ///
2746    /// ## Example: Using `action!`
2747    ///
2748    /// ```
2749    /// use editor_types::prelude::*;
2750    /// use editor_types::{action, Action, SelectionAction};
2751    ///
2752    /// let style = SelectionResizeStyle::Restart;
2753    /// let target = EditTarget::CurrentPosition;
2754    /// let act: Action = SelectionAction::Resize(style, target.clone()).into();
2755    /// assert_eq!(act, action!("selection resize -s restart -t {target}"));
2756    /// ```
2757    Restart,
2758}
2759
2760/// When focusing on the command bar, this is the type of command that should be submitted.
2761#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2762pub enum CommandType {
2763    /// Prompt the user for a command.
2764    Command,
2765
2766    /// Prompt the user for a search query.
2767    Search,
2768}
2769
2770/// What history items to recall during [PromptAction::Recall].
2771#[derive(Clone, Debug, Eq, PartialEq)]
2772pub enum RecallFilter {
2773    /// Include all items in the prompt's history.
2774    ///
2775    /// ## Example: Using `action!`
2776    ///
2777    /// ```
2778    /// use editor_types::prelude::*;
2779    /// use editor_types::{action, Action, PromptAction};
2780    ///
2781    /// let filter = RecallFilter::All;
2782    /// let act: Action = PromptAction::Recall(filter.clone(), MoveDir1D::Next, Count::Contextual).into();
2783    /// assert_eq!(act, action!("prompt recall -d next -c ctx -F all"));
2784    /// ```
2785    All,
2786
2787    /// Only include items whose prefix matches the initially typed text.
2788    ///
2789    /// ## Example: Using `action!`
2790    ///
2791    /// ```
2792    /// use editor_types::prelude::*;
2793    /// use editor_types::{action, Action, PromptAction};
2794    ///
2795    /// let filter = RecallFilter::PrefixMatch;
2796    /// let act: Action = PromptAction::Recall(filter.clone(), MoveDir1D::Next, Count::Contextual).into();
2797    ///
2798    /// // All of these are equivalent:
2799    /// assert_eq!(act, action!("prompt recall -d next -c ctx -F prefix-match"));
2800    /// assert_eq!(act, action!("prompt recall -d next -c ctx -F prefix"));
2801    /// assert_eq!(act, action!("prompt recall -d next -c ctx -F {filter}"));
2802    /// ```
2803    PrefixMatch,
2804}
2805
2806/// This specifies which list of cursors to use when jumping, the change list or the jump list.
2807#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2808pub enum PositionList {
2809    /// The change list contains positions where changes were previously made.
2810    ///
2811    /// ## Example: Using `action!`
2812    ///
2813    /// ```
2814    /// use editor_types::prelude::*;
2815    /// use editor_types::{action, Action, InsertTextAction};
2816    ///
2817    /// let list = PositionList::ChangeList;
2818    /// let count = Count::Contextual;
2819    /// let act: Action = Action::Jump(list, MoveDir1D::Next, count);
2820    /// assert_eq!(act, action!("jump -t change-list -d next"));
2821    /// ```
2822    ChangeList,
2823
2824    /// The jump list contains positions where the cursor was placed before jumping to a new
2825    /// location in the document.
2826    ///
2827    /// ## Example: Using `action!`
2828    ///
2829    /// ```
2830    /// use editor_types::prelude::*;
2831    /// use editor_types::{action, Action, InsertTextAction};
2832    ///
2833    /// let list = PositionList::JumpList;
2834    /// let count = Count::Contextual;
2835    /// let act: Action = Action::Jump(list, MoveDir1D::Next, count);
2836    /// assert_eq!(act, action!("jump -t jump-list -d next"));
2837    /// ```
2838    JumpList,
2839}
2840
2841/// This specifies the behaviour of entering and backspacing over characters.
2842#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2843pub enum InsertStyle {
2844    /// This specifies that typed characters should leave existing ones as is, and backspacing
2845    /// should remove characters.
2846    Insert,
2847
2848    /// This specifies that typed characters should replace existing ones, and backspacing should
2849    /// restore any overwritten characters.
2850    Replace,
2851}
2852
2853/// A character.
2854#[derive(Clone, Debug, Eq, PartialEq)]
2855pub enum Char {
2856    /// An exact character.
2857    Single(char),
2858    /// A digraph sequence.
2859    Digraph(char, char),
2860    /// A terminal control sequence.
2861    CtrlSeq(String),
2862    /// Copy a character from the same column in the previous or next line.
2863    CopyLine(MoveDir1D),
2864}
2865
2866impl From<char> for Char {
2867    fn from(c: char) -> Self {
2868        Char::Single(c)
2869    }
2870}
2871
2872/// Locations for temporarily storing text shared between buffers.
2873#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2874#[non_exhaustive]
2875pub enum Register {
2876    /// The default register.
2877    ///
2878    /// For example, `""` in Vim.
2879    Unnamed,
2880
2881    /// The default macro register.
2882    ///
2883    /// For example, `"@` in Kakoune.
2884    UnnamedMacro,
2885
2886    /// The default cursor group register.
2887    ///
2888    ///
2889    /// For example, `"^` in Kakoune.
2890    UnnamedCursorGroup,
2891
2892    /// Recently deleted text.
2893    ///
2894    /// For example, `"[1-9]` in Vim.
2895    RecentlyDeleted(usize),
2896
2897    /// Most recently deleted text that was shorted than a line.
2898    ///
2899    /// For example, `"-` in Vim.
2900    SmallDelete,
2901
2902    /// A register containing the last inserted text.
2903    ///
2904    /// For example, `".` in Vim.
2905    LastInserted,
2906
2907    /// A register containing the last value entered for a [CommandType].
2908    ///
2909    /// For example, `":` and `"/` in Vim.
2910    LastCommand(CommandType),
2911
2912    /// A register containing the last copied text.
2913    ///
2914    /// For eample, `"0` in Vim.
2915    LastYanked,
2916
2917    /// A register named by `char`.
2918    ///
2919    /// The index of the most recent deletion is 0, the second most recent deletion is 1, and so
2920    /// on.
2921    ///
2922    /// For example, `"[a-zA-Z]` in Vim.
2923    Named(char),
2924
2925    /// A read-only register containing the alternate buffer name.
2926    ///
2927    /// For example, `"#` in Vim.
2928    AltBufName,
2929
2930    /// A read-only register containing the current buffer name.
2931    ///
2932    /// For example, `"%` in Vim.
2933    CurBufName,
2934
2935    /// A register that discards all content written to it.
2936    ///
2937    /// For example, `"_` in Vim.
2938    Blackhole,
2939
2940    /// A register representing the windowing environment's most recently selected text.
2941    ///
2942    /// For example, `"*` in Vim, or what clicking the mouse's middle button pastes in X and
2943    /// Wayland.
2944    SelectionPrimary,
2945
2946    /// A register representing the windowing environment's most recently copied text.
2947    ///
2948    /// For example, `"+` in Vim, or what the keyboard shortcut pastes in X and Wayland.
2949    SelectionClipboard,
2950}
2951
2952impl Register {
2953    /// Indicates whether a given register is allowed to store cursor groups.
2954    pub fn is_cursor_storage(&self) -> bool {
2955        match self {
2956            Register::Named(_) => true,
2957            Register::Blackhole => true,
2958            Register::UnnamedCursorGroup => true,
2959
2960            Register::Unnamed => false,
2961            Register::UnnamedMacro => false,
2962            Register::RecentlyDeleted(_) => false,
2963            Register::SmallDelete => false,
2964            Register::LastCommand(_) => false,
2965            Register::LastInserted => false,
2966            Register::LastYanked => false,
2967            Register::AltBufName => false,
2968            Register::CurBufName => false,
2969            Register::SelectionPrimary => false,
2970            Register::SelectionClipboard => false,
2971        }
2972    }
2973}
2974
2975/// This specifies either the shape of a visual selection, or a forced motion.
2976#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2977pub enum TargetShape {
2978    /// A series of characters.
2979    ///
2980    /// During a selection, the two points indicate the start and end columns.
2981    ///
2982    /// ## Example: Using `action!`
2983    ///
2984    /// ```
2985    /// use editor_types::prelude::*;
2986    /// use editor_types::{action, Action, InsertTextAction};
2987    ///
2988    /// let shape = TargetShape::CharWise;
2989    /// let count = Count::Contextual;
2990    /// let act: Action = InsertTextAction::OpenLine(shape, MoveDir1D::Next, count).into();
2991    ///
2992    /// // All of these are equivalent:
2993    /// assert_eq!(act, action!("insert open-line -S charwise -d next"));
2994    /// assert_eq!(act, action!("insert open-line -S char -d next"));
2995    /// ```
2996    CharWise,
2997
2998    /// A series of lines.
2999    ///
3000    /// During a selection, the two points indicate the start and end lines.
3001    ///
3002    /// ## Example: Using `action!`
3003    ///
3004    /// ```
3005    /// use editor_types::prelude::*;
3006    /// use editor_types::{action, Action, InsertTextAction};
3007    ///
3008    /// let shape = TargetShape::LineWise;
3009    /// let count = Count::Contextual;
3010    /// let act: Action = InsertTextAction::OpenLine(shape, MoveDir1D::Next, count).into();
3011    ///
3012    /// // All of these are equivalent:
3013    /// assert_eq!(act, action!("insert open-line -S linewise -d next"));
3014    /// assert_eq!(act, action!("insert open-line -S line -d next"));
3015    /// ```
3016    LineWise,
3017
3018    /// A block of characters.
3019    ///
3020    /// During a selection, the two points indicate opposite corners.
3021    ///
3022    /// ## Example: Using `action!`
3023    ///
3024    /// ```
3025    /// use editor_types::prelude::*;
3026    /// use editor_types::{action, Action, InsertTextAction};
3027    ///
3028    /// let shape = TargetShape::BlockWise;
3029    /// let count = Count::Contextual;
3030    /// let act: Action = InsertTextAction::OpenLine(shape, MoveDir1D::Next, count).into();
3031    ///
3032    /// // All of these are equivalent:
3033    /// assert_eq!(act, action!("insert open-line -S blockwise -d next"));
3034    /// assert_eq!(act, action!("insert open-line -S block -d next"));
3035    /// ```
3036    BlockWise,
3037}
3038
3039bitflags! {
3040    /// Bitmask that specifies what shapes are targeted by an action.
3041    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
3042    pub struct TargetShapeFilter: u32 {
3043        /// Match no shapes.
3044        const NONE = 0b00000000;
3045
3046        /// Match all shapes.
3047        const ALL = 0b00000111;
3048
3049        /// Match [TargetShape::CharWise].
3050        const CHAR = 0b00000001;
3051
3052        /// Match [TargetShape::LineWise].
3053        const LINE = 0b00000010;
3054
3055        /// Match [TargetShape::BlockWise].
3056        const BLOCK = 0b00000100;
3057    }
3058}
3059
3060impl TargetShapeFilter {
3061    /// Check whether this filter applies to a given [TargetShape].
3062    pub fn matches(&self, shape: &TargetShape) -> bool {
3063        match shape {
3064            TargetShape::CharWise => self.contains(TargetShapeFilter::CHAR),
3065            TargetShape::LineWise => self.contains(TargetShapeFilter::LINE),
3066            TargetShape::BlockWise => self.contains(TargetShapeFilter::BLOCK),
3067        }
3068    }
3069}
3070
3071impl From<TargetShape> for TargetShapeFilter {
3072    fn from(shape: TargetShape) -> Self {
3073        match shape {
3074            TargetShape::CharWise => TargetShapeFilter::CHAR,
3075            TargetShape::LineWise => TargetShapeFilter::LINE,
3076            TargetShape::BlockWise => TargetShapeFilter::BLOCK,
3077        }
3078    }
3079}
3080
3081/// Action to take on targets when filtering with a regular expression.
3082#[derive(Debug, Clone, Copy, Eq, PartialEq)]
3083pub enum MatchAction {
3084    /// Keep targets of the regular expression.
3085    ///
3086    /// ## Example: Using `action!`
3087    ///
3088    /// ```
3089    /// use editor_types::prelude::*;
3090    /// use editor_types::{action, Action, SelectionAction};
3091    ///
3092    /// let act = SelectionAction::Filter(MatchAction::Keep);
3093    /// let split: Action = action!("selection filter -F keep");
3094    /// assert_eq!(split, act.into());
3095    /// ```
3096    Keep,
3097
3098    /// Remove targets of the regular expression.
3099    ///
3100    /// ## Example: Using `action!`
3101    ///
3102    /// ```
3103    /// use editor_types::prelude::*;
3104    /// use editor_types::{action, Action, SelectionAction};
3105    ///
3106    /// let act = SelectionAction::Filter(MatchAction::Drop);
3107    /// let split: Action = action!("selection filter -F drop");
3108    /// assert_eq!(split, act.into());
3109    /// ```
3110    Drop,
3111}
3112
3113impl MatchAction {
3114    /// Whether this action is [MatchAction::Keep].
3115    pub fn is_keep(&self) -> bool {
3116        matches!(self, MatchAction::Keep)
3117    }
3118
3119    /// Whether this action is [MatchAction::Drop].
3120    pub fn is_drop(&self) -> bool {
3121        matches!(self, MatchAction::Drop)
3122    }
3123}
3124
3125/// Methods for determining the start and end of a [RangeSpec].
3126#[derive(Clone, Debug, Eq, PartialEq)]
3127pub enum RangeEndingType {
3128    /// A specific line number.
3129    Absolute(Count),
3130
3131    /// All lines.
3132    All,
3133
3134    /// The current line.
3135    Current,
3136
3137    /// The last line.
3138    Last,
3139
3140    /// The position of a given [Mark].
3141    Mark(Specifier<Mark>),
3142
3143    /// The line matching a search using the last value of [CommandType::Search].
3144    Search(MoveDir1D),
3145
3146    /// Perform a search using the last substitution pattern.
3147    SubPatSearch(MoveDir1D),
3148
3149    /// No line was specified.
3150    Unspecified,
3151}
3152
3153/// Modifier to a range ending.
3154#[non_exhaustive]
3155#[derive(Clone, Debug, Eq, PartialEq)]
3156pub enum RangeEndingModifier {
3157    /// Offset the end of a range by [*n*](Count) lines.
3158    Offset(MoveDir1D, Count),
3159}
3160
3161/// One of the sides of a range.
3162#[derive(Clone, Debug, Eq, PartialEq)]
3163pub struct RangeEnding(pub RangeEndingType, pub Vec<RangeEndingModifier>);
3164
3165/// Position to begin a search in a range.
3166#[derive(Clone, Debug, Eq, PartialEq)]
3167pub enum RangeSearchInit {
3168    /// Start from current cursor position.
3169    Cursor,
3170
3171    /// Start from the beginning of the range.
3172    Start,
3173}
3174
3175/// A range specification.
3176#[derive(Clone, Debug, Eq, PartialEq)]
3177pub enum RangeSpec {
3178    /// A range specification where only one end of the range was given.
3179    Single(RangeEnding),
3180
3181    /// A range specification where both ends of the range were given.
3182    Double(RangeEnding, RangeEnding, RangeSearchInit),
3183}
3184
3185/// Trait for objects that allow toggling line wrapping.
3186pub trait Wrappable {
3187    /// Set whether or not displayed lines should be wrapped.
3188    fn set_wrap(&mut self, wrap: bool);
3189}
3190
3191/// Information about what portion of a buffer is being displayed in a window.
3192pub struct ViewportContext<Cursor> {
3193    /// The line and column offset into the buffer shown at the upper-left hand corner of the
3194    /// window.
3195    pub corner: Cursor,
3196
3197    /// Dimensions of the window.
3198    pub dimensions: (usize, usize),
3199
3200    /// Whether or not displayed lines are being wrapped.
3201    pub wrap: bool,
3202}
3203
3204impl<Cursor: Default> ViewportContext<Cursor> {
3205    /// Create a new context for describing a viewport.
3206    pub fn new() -> Self {
3207        ViewportContext {
3208            corner: Cursor::default(),
3209            dimensions: (0, 0),
3210            wrap: false,
3211        }
3212    }
3213
3214    /// Get the viewport height.
3215    pub fn get_height(&self) -> usize {
3216        self.dimensions.1
3217    }
3218
3219    /// Get the viewport width.
3220    pub fn get_width(&self) -> usize {
3221        self.dimensions.0
3222    }
3223}
3224
3225impl<Cursor: Default> Default for ViewportContext<Cursor> {
3226    fn default() -> Self {
3227        ViewportContext::new()
3228    }
3229}
3230
3231impl<Cursor: Clone> Clone for ViewportContext<Cursor> {
3232    fn clone(&self) -> Self {
3233        ViewportContext {
3234            corner: self.corner.clone(),
3235            dimensions: self.dimensions,
3236            wrap: self.wrap,
3237        }
3238    }
3239}
3240
3241impl<Cursor: Wrappable> Wrappable for ViewportContext<Cursor> {
3242    fn set_wrap(&mut self, wrap: bool) {
3243        self.wrap = wrap;
3244        self.corner.set_wrap(wrap);
3245    }
3246}
3247
3248/// This context object wraps information used when calculating what text covered by cursor
3249/// movements.
3250pub struct CursorMovementsContext<'a, Cursor> {
3251    /// What operation this movement is being done as part of.
3252    ///
3253    /// Certain movements, like [MoveType::WordBegin], behave different depending on the action.
3254    pub action: &'a EditAction,
3255
3256    /// Information about the user's view of the text, since this impacts movements that rely on
3257    /// how the text is displayed, such as [MoveType::ScreenLine].
3258    pub view: &'a ViewportContext<Cursor>,
3259
3260    /// The editing context contains information about the current [InsertStyle], as well as the
3261    /// user-supplied [Count].
3262    pub context: &'a EditContext,
3263}
3264
3265/// Trait for objects capable of calculating contextual offsets from a cursor.
3266pub trait CursorMovements<Cursor> {
3267    /// Calculate the position of the first word on the line of the provided cursor.
3268    fn first_word(&self, cursor: &Cursor, ctx: &CursorMovementsContext<'_, Cursor>) -> Cursor;
3269
3270    /// Calculate the position of the cursor after performing a movement.
3271    fn movement(
3272        &self,
3273        cursor: &Cursor,
3274        movement: &MoveType,
3275        count: &Count,
3276        ctx: &CursorMovementsContext<'_, Cursor>,
3277    ) -> Option<Cursor>;
3278
3279    /// Calculate a cursor range from the given cursor to the location after performing the
3280    /// given movement.
3281    fn range_of_movement(
3282        &self,
3283        cursor: &Cursor,
3284        movement: &MoveType,
3285        count: &Count,
3286        ctx: &CursorMovementsContext<'_, Cursor>,
3287    ) -> Option<EditRange<Cursor>>;
3288
3289    /// Calculate a cursor range based on a given cursor position and a [RangeType].
3290    fn range(
3291        &self,
3292        cursor: &Cursor,
3293        range: &RangeType,
3294        inclusive: bool,
3295        count: &Count,
3296        ctx: &CursorMovementsContext<'_, Cursor>,
3297    ) -> Option<EditRange<Cursor>>;
3298}
3299
3300/// Trait for objects capable of searching text.
3301pub trait CursorSearch<Cursor> {
3302    /// Search for a specific character.
3303    fn find_char(
3304        &self,
3305        cursor: &Cursor,
3306        inclusive: bool,
3307        dir: MoveDir1D,
3308        multiline: bool,
3309        needle: char,
3310        count: usize,
3311    ) -> Option<Cursor>;
3312
3313    /// Find matches for a regular expression within a range.
3314    fn find_matches(&self, start: &Cursor, end: &Cursor, needle: &Regex) -> Vec<EditRange<Cursor>>;
3315
3316    /// Search for a regular expression.
3317    fn find_regex(
3318        &self,
3319        cursor: &Cursor,
3320        dir: MoveDir1D,
3321        needle: &Regex,
3322        count: usize,
3323    ) -> Option<EditRange<Cursor>>;
3324}
3325
3326/// Trait for directions capable of being flipped.
3327pub trait Flip {
3328    /// Return the flipped representation of the value.
3329    fn flip(&self) -> Self;
3330}
3331
3332impl Flip for MoveDir1D {
3333    fn flip(&self) -> MoveDir1D {
3334        match self {
3335            MoveDir1D::Previous => MoveDir1D::Next,
3336            MoveDir1D::Next => MoveDir1D::Previous,
3337        }
3338    }
3339}
3340
3341impl Flip for MoveDir2D {
3342    fn flip(&self) -> MoveDir2D {
3343        match self {
3344            MoveDir2D::Left => MoveDir2D::Right,
3345            MoveDir2D::Right => MoveDir2D::Left,
3346            MoveDir2D::Up => MoveDir2D::Down,
3347            MoveDir2D::Down => MoveDir2D::Up,
3348        }
3349    }
3350}
3351
3352impl MoveDir2D {
3353    /// Returns the [Axis] that the direction moves along.
3354    pub fn axis(&self) -> Axis {
3355        match self {
3356            MoveDir2D::Left => Axis::Horizontal,
3357            MoveDir2D::Right => Axis::Horizontal,
3358            MoveDir2D::Up => Axis::Vertical,
3359            MoveDir2D::Down => Axis::Vertical,
3360        }
3361    }
3362}
3363
3364impl std::ops::Not for InsertStyle {
3365    type Output = Self;
3366
3367    fn not(self) -> Self::Output {
3368        match self {
3369            InsertStyle::Insert => InsertStyle::Replace,
3370            InsertStyle::Replace => InsertStyle::Insert,
3371        }
3372    }
3373}
3374
3375impl MoveType {
3376    /// Returns `true` if this is an inclusive motion.
3377    pub fn is_inclusive_motion(&self) -> bool {
3378        match self {
3379            MoveType::BufferPos(_) => true,
3380            MoveType::FinalNonBlank(_) => true,
3381            MoveType::ItemMatch => true,
3382            MoveType::LineColumnOffset => true,
3383            MoveType::WordEnd(_, _) => true,
3384
3385            MoveType::BufferByteOffset => false,
3386            MoveType::BufferLineOffset => false,
3387            MoveType::BufferLinePercent => false,
3388            MoveType::Column(_, _) => false,
3389            MoveType::FirstWord(_) => false,
3390            MoveType::Line(_) => false,
3391            MoveType::LinePercent => false,
3392            MoveType::LinePos(_) => false,
3393            MoveType::ParagraphBegin(_) => false,
3394            MoveType::ScreenFirstWord(_) => false,
3395            MoveType::ScreenLine(_) => false,
3396            MoveType::ScreenLinePos(_) => false,
3397            MoveType::ViewportPos(_) => false,
3398            MoveType::SectionBegin(_) => false,
3399            MoveType::SectionEnd(_) => false,
3400            MoveType::SentenceBegin(_) => false,
3401            MoveType::WordBegin(_, _) => false,
3402        }
3403    }
3404
3405    /// Returns `true` if this is a motion that causes cursor positions to be saved to
3406    /// [PositionList::JumpList].
3407    pub fn is_jumping(&self) -> bool {
3408        match self {
3409            MoveType::BufferByteOffset => true,
3410            MoveType::BufferLineOffset => true,
3411            MoveType::BufferLinePercent => true,
3412            MoveType::BufferPos(_) => true,
3413            MoveType::ItemMatch => true,
3414            MoveType::ParagraphBegin(_) => true,
3415            MoveType::ViewportPos(_) => true,
3416            MoveType::SectionBegin(_) => true,
3417            MoveType::SentenceBegin(_) => true,
3418
3419            MoveType::Column(_, _) => false,
3420            MoveType::FinalNonBlank(_) => false,
3421            MoveType::FirstWord(_) => false,
3422            MoveType::LineColumnOffset => false,
3423            MoveType::Line(_) => false,
3424            MoveType::LinePercent => false,
3425            MoveType::LinePos(_) => false,
3426            MoveType::ScreenFirstWord(_) => false,
3427            MoveType::ScreenLine(_) => false,
3428            MoveType::ScreenLinePos(_) => false,
3429            MoveType::SectionEnd(_) => false,
3430            MoveType::WordBegin(_, _) => false,
3431            MoveType::WordEnd(_, _) => false,
3432        }
3433    }
3434
3435    /// Returns the shape of the text selected by this movement when editing.
3436    pub fn shape(&self) -> TargetShape {
3437        match self {
3438            MoveType::BufferLineOffset => TargetShape::LineWise,
3439            MoveType::BufferLinePercent => TargetShape::LineWise,
3440            MoveType::BufferPos(_) => TargetShape::LineWise,
3441            MoveType::FirstWord(_) => TargetShape::LineWise,
3442            MoveType::Line(_) => TargetShape::LineWise,
3443            MoveType::ViewportPos(_) => TargetShape::LineWise,
3444            MoveType::SectionBegin(_) => TargetShape::LineWise,
3445            MoveType::SectionEnd(_) => TargetShape::LineWise,
3446
3447            MoveType::BufferByteOffset => TargetShape::CharWise,
3448            MoveType::Column(_, _) => TargetShape::CharWise,
3449            MoveType::FinalNonBlank(_) => TargetShape::CharWise,
3450            MoveType::ItemMatch => TargetShape::CharWise,
3451            MoveType::LineColumnOffset => TargetShape::CharWise,
3452            MoveType::LinePercent => TargetShape::CharWise,
3453            MoveType::LinePos(_) => TargetShape::CharWise,
3454            MoveType::ParagraphBegin(_) => TargetShape::CharWise,
3455            MoveType::ScreenFirstWord(_) => TargetShape::CharWise,
3456            MoveType::ScreenLinePos(_) => TargetShape::CharWise,
3457            MoveType::ScreenLine(_) => TargetShape::CharWise,
3458            MoveType::SentenceBegin(_) => TargetShape::CharWise,
3459            MoveType::WordBegin(_, _) => TargetShape::CharWise,
3460            MoveType::WordEnd(_, _) => TargetShape::CharWise,
3461        }
3462    }
3463}
3464
3465impl MoveDirMod {
3466    /// Modify a given direction.
3467    pub fn resolve(&self, dir: &MoveDir1D) -> MoveDir1D {
3468        match self {
3469            MoveDirMod::Same => *dir,
3470            MoveDirMod::Flip => dir.flip(),
3471            MoveDirMod::Exact(exact) => *exact,
3472        }
3473    }
3474}
3475
3476impl From<MoveDir1D> for MoveDirMod {
3477    fn from(dir: MoveDir1D) -> Self {
3478        MoveDirMod::Exact(dir)
3479    }
3480}
3481
3482/// Information to show the user at the bottom of the screen after an action.
3483#[derive(Clone, Debug, Eq, PartialEq)]
3484pub enum InfoMessage {
3485    /// Print a simple, informational message on the status line.
3486    Message(String),
3487
3488    /// Use an interactive pager to show the user some information.
3489    ///
3490    /// If you're using [keybindings], then you can handle this using [Pager] and
3491    /// [BindingMachine::run_dialog].
3492    ///
3493    /// [Pager]: keybindings::dialog::Pager
3494    /// [BindingMachine::run_dialog]: keybindings::BindingMachine::run_dialog
3495    Pager(String),
3496}
3497
3498impl Display for InfoMessage {
3499    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
3500        match self {
3501            InfoMessage::Message(s) | InfoMessage::Pager(s) => write!(f, "{}", s),
3502        }
3503    }
3504}
3505
3506impl From<&str> for InfoMessage {
3507    fn from(msg: &str) -> Self {
3508        InfoMessage::from(msg.to_string())
3509    }
3510}
3511
3512impl From<String> for InfoMessage {
3513    fn from(msg: String) -> Self {
3514        InfoMessage::Message(msg)
3515    }
3516}
3517
3518/// An optional, information message provided during editing.
3519pub type EditInfo = Option<InfoMessage>;