Skip to main content

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