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::EditAction;
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    Cursor,
70
71    /// Paste text before the selection's start, or after its end.
72    Side(MoveDir1D),
73
74    /// Replace selected text with register contents.
75    Replace,
76}
77
78/// The source to search for completion candidates.
79#[derive(Clone, Debug, Eq, PartialEq)]
80pub enum CompletionScope {
81    /// Only use completion candidates from the current buffer.
82    Buffer,
83
84    /// Use completion candidates available from all buffers.
85    Global,
86}
87
88/// What type of phrase we are completing.
89#[derive(Clone, Debug, Eq, PartialEq)]
90pub enum CompletionSelection {
91    /// Navigate through the list of completion candidates.
92    List(MoveDir1D),
93
94    /// Generate completion candidates, but don't select any from the list.
95    None,
96
97    /// Complete only the longest common prefix from the completion candidates.
98    Prefix,
99
100    /// If there is only a single completion candidate, select it.
101    Single,
102}
103
104/// What type of phrase we are completing.
105#[derive(Clone, Debug, Eq, PartialEq)]
106pub enum CompletionType {
107    /// Determine what to complete by the buffer context.
108    Auto,
109
110    /// Complete a filename.
111    File,
112
113    /// Complete the rest of the line.
114    Line(CompletionScope),
115
116    /// Complete the current word.
117    Word(CompletionScope),
118}
119
120/// How to display completion candidates.
121#[derive(Clone, Debug, Eq, PartialEq)]
122pub enum CompletionDisplay {
123    /// Don't display candidates.
124    None,
125
126    /// Display candidates in a bar above the command bar.
127    Bar,
128
129    /// Display candidates in a pop-up list.
130    List,
131}
132
133/// Specify what is targeted by an editing action.
134#[derive(Clone, Debug, Eq, PartialEq)]
135#[non_exhaustive]
136pub enum EditTarget {
137    /// Move to one of the sides of a range.
138    Boundary(RangeType, bool, MoveTerminus, Count),
139
140    /// Target the current cursor position.
141    CurrentPosition,
142
143    /// Move to the line and column of a [Mark].
144    CharJump(Specifier<Mark>),
145
146    /// Move to the first word of the line that [Mark] is on.
147    LineJump(Specifier<Mark>),
148
149    /// Target the text between the current cursor position and the end of a motion.
150    Motion(MoveType, Count),
151
152    /// Target a range of text around the cursor.
153    ///
154    /// [bool] indicates if this is an inclusive range, when applicable to the [RangeType].
155    Range(RangeType, bool, Count),
156
157    /// Target the text between the current cursor position and the end of a search.
158    ///
159    /// The [MoveDirMod] parameter modifies the search direction.
160    Search(SearchType, MoveDirMod, Count),
161
162    /// Target the visually selected text.
163    Selection,
164}
165
166impl EditTarget {
167    /// Returns `true` if this is a target that causes cursor positions to be saved to
168    /// [PositionList::JumpList].
169    pub fn is_jumping(&self) -> bool {
170        match self {
171            EditTarget::Boundary(..) => true,
172            EditTarget::CurrentPosition => false,
173            EditTarget::CharJump(_) => true,
174            EditTarget::LineJump(_) => true,
175            EditTarget::Motion(mt, _) => mt.is_jumping(),
176            EditTarget::Range(..) => true,
177            EditTarget::Search(st, ..) => st.is_jumping(),
178            EditTarget::Selection => false,
179        }
180    }
181}
182
183impl From<MoveType> for EditTarget {
184    fn from(mt: MoveType) -> Self {
185        EditTarget::Motion(mt, Count::Contextual)
186    }
187}
188
189impl From<RangeType> for EditTarget {
190    fn from(mt: RangeType) -> Self {
191        EditTarget::Range(mt, true, Count::Contextual)
192    }
193}
194
195/// Determines where to leave the cursor after editing text.
196#[derive(Clone, Copy, Debug, Eq, PartialEq)]
197pub enum CursorEnd {
198    /// Keep the current cursor position as best as possible.
199    Keep,
200
201    /// Place the cursor at the start of the [EditTarget].
202    Start,
203
204    /// Place the cursor at the end of the [EditTarget].
205    End,
206
207    /// Select from the start to the end of the [EditTarget].
208    Selection,
209
210    /// Use the default cursor end position for the operation.
211    Auto,
212}
213
214/// Description of a textual range within a buffer.
215#[derive(Clone, Debug, Eq, PartialEq)]
216pub struct EditRange<Cursor> {
217    /// The start of the range.
218    pub start: Cursor,
219
220    /// The end of the range.
221    pub end: Cursor,
222
223    /// The default shape to interpret the range as. This might be overriden by
224    /// [EditContext::get_target_shape].
225    pub shape: TargetShape,
226
227    /// Whether to include the character at the end Cursor when interpreted as a CharWise range.
228    pub inclusive: bool,
229}
230
231impl<Cursor: Ord> EditRange<Cursor> {
232    /// Create a new editing range.
233    pub fn new(a: Cursor, b: Cursor, shape: TargetShape, inclusive: bool) -> Self {
234        let (start, end) = sort2(a, b);
235
236        EditRange { start, end, shape, inclusive }
237    }
238
239    /// Create a new inclusive editing range.
240    pub fn inclusive(a: Cursor, b: Cursor, shape: TargetShape) -> Self {
241        Self::new(a, b, shape, true)
242    }
243
244    /// Create a new exclusive editing range.
245    pub fn exclusive(a: Cursor, b: Cursor, shape: TargetShape) -> Self {
246        Self::new(a, b, shape, false)
247    }
248}
249
250/// Different action sequences that can be repeated.
251#[derive(Clone, Debug, Eq, Hash, PartialEq)]
252pub enum RepeatType {
253    /// A sequence of changes made to a buffer.
254    EditSequence,
255
256    /// The last [Action](crate::Action) done.
257    LastAction,
258
259    /// The last selection resize made in a buffer.
260    LastSelection,
261}
262
263/// Specify a range within the text around the current cursor position.
264#[derive(Clone, Debug, Eq, PartialEq)]
265pub enum SearchType {
266    /// Search for the character indicated by [EditContext::get_search_char].
267    ///
268    /// [bool] controls whether the search should continue across line boundaries.
269    Char(bool),
270
271    /// Search for a regular expression.
272    Regex,
273
274    /// Search for the word currently under the cursor, and update the last [CommandType::Search]
275    /// value in the application's register store.
276    ///
277    /// [bool] controls whether matches should be checked for using word boundaries.
278    Word(WordStyle, bool),
279}
280
281impl SearchType {
282    /// Returns `true` if this is an inclusive motion.
283    pub fn is_inclusive_motion(&self) -> bool {
284        match self {
285            SearchType::Char(..) => true,
286            SearchType::Regex => false,
287            SearchType::Word(..) => false,
288        }
289    }
290
291    /// Returns `true` if this is a search that causes cursor positions to be saved to
292    /// [PositionList::JumpList].
293    fn is_jumping(&self) -> bool {
294        match self {
295            SearchType::Char(..) => false,
296            SearchType::Regex => true,
297            SearchType::Word(..) => true,
298        }
299    }
300}
301
302/// The different ways of grouping a buffer's contents into words.
303#[derive(Clone, Debug, Eq, PartialEq)]
304pub enum WordStyle {
305    /// A run of alphanumeric characters.
306    AlphaNum,
307
308    /// A sequence of non-blank characters.
309    ///
310    /// An empty line is also a Big word. Vim calls this a `WORD`.
311    Big,
312
313    /// A sequence of characters that match a test function.
314    CharSet(fn(char) -> bool),
315
316    /// A name of a directory or file.
317    FileName,
318
319    /// A path to a directory or file.
320    FilePath,
321
322    /// Either a sequence of alphanumeric characters and underscores, or a sequence of other
323    /// non-blank characters.
324    ///
325    /// An empty line is also a Little word.
326    Little,
327
328    /// A run of non-alphanumeric characters.
329    NonAlphaNum,
330
331    /// A run of digits in the given base, with an optional leading hyphen.
332    Number(Radix),
333
334    /// A run of blank characters.
335    ///
336    /// [bool] controls whether this crosses line boundaries.
337    Whitespace(bool),
338}
339
340impl WordStyle {
341    /// Whether this [WordStyle] can ever contain the character `c`.
342    pub fn contains(&self, c: char) -> bool {
343        match self {
344            WordStyle::AlphaNum => is_word_char(c),
345            WordStyle::NonAlphaNum => !is_word_char(c),
346            WordStyle::Big => !is_space_char(c),
347            WordStyle::CharSet(f) => f(c),
348            WordStyle::FileName => is_filename_char(c),
349            WordStyle::FilePath => is_filepath_char(c),
350            WordStyle::Little => is_word_char(c) || is_keyword(c),
351            WordStyle::Number(radix) => radix.contains(c),
352            WordStyle::Whitespace(true) => is_space_char(c),
353            WordStyle::Whitespace(false) => is_horizontal_space(c),
354        }
355    }
356}
357
358impl BoundaryTest for WordStyle {
359    fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool {
360        match self {
361            WordStyle::AlphaNum => {
362                if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
363                    // Last character is counted when moving forward.
364                    return true;
365                } else if let Some(before) = ctx.before {
366                    let befwc = is_word_char(before);
367                    let curwc = is_word_char(ctx.current);
368
369                    return !befwc && curwc;
370                } else {
371                    // First character is always counted.
372                    return true;
373                }
374            },
375            WordStyle::NonAlphaNum => {
376                if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
377                    // Last character is counted when moving forward.
378                    return true;
379                } else if let Some(before) = ctx.before {
380                    let befwc = is_word_char(before);
381                    let curwc = is_word_char(ctx.current);
382
383                    return befwc && !curwc;
384                } else {
385                    // First character is always counted.
386                    return true;
387                }
388            },
389            WordStyle::Big => {
390                if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
391                    // Last character is counted when moving forward.
392                    return true;
393                } else if let Some(before) = ctx.before {
394                    let befws = is_space_char(before);
395                    let curws = is_space_char(ctx.current);
396                    let curnl = is_newline(ctx.current);
397
398                    // The final word beginning is calculated differently during an operation.
399                    let last = !ctx.motion && ctx.count == 1;
400
401                    return (last && curnl) || (befws && !curws);
402                } else {
403                    // First character is always counted.
404                    return true;
405                }
406            },
407            WordStyle::CharSet(f) => {
408                if let Some(before) = ctx.before {
409                    f(ctx.current) && !f(before)
410                } else {
411                    f(ctx.current)
412                }
413            },
414            WordStyle::FileName => {
415                if let Some(before) = ctx.before {
416                    is_filename_char(ctx.current) && !is_filename_char(before)
417                } else {
418                    is_filename_char(ctx.current)
419                }
420            },
421            WordStyle::FilePath => {
422                if let Some(before) = ctx.before {
423                    is_filepath_char(ctx.current) && !is_filepath_char(before)
424                } else {
425                    is_filepath_char(ctx.current)
426                }
427            },
428            WordStyle::Little => {
429                if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
430                    // Last character is counted when moving forward.
431                    return true;
432                } else if let Some(before) = ctx.before {
433                    let befwc = is_word_char(before);
434                    let befkw = is_keyword(before);
435                    let curwc = is_word_char(ctx.current);
436                    let curkw = is_keyword(ctx.current);
437                    let curnl = is_newline(ctx.current);
438
439                    // The final word beginning is calculated differently during an operation.
440                    let last = !ctx.motion && ctx.count == 1;
441
442                    return (last && curnl) ||
443                        (befwc && curkw) ||
444                        (befkw && curwc) ||
445                        (!befwc && curwc) ||
446                        (!befkw && curkw);
447                } else {
448                    // First character is always counted.
449                    return true;
450                }
451            },
452            WordStyle::Number(radix) => {
453                let cn = radix.contains(ctx.current);
454
455                if ctx.current == '-' {
456                    // A hyphen is only the start of a number if a digit follows it.
457                    matches!(ctx.after, Some(c) if radix.contains(c))
458                } else if let Some(before) = ctx.before {
459                    // Not preceded by a hyphen or digit.
460                    cn && before != '-' && !radix.contains(before)
461                } else {
462                    // First character counts if it's a digit.
463                    cn
464                }
465            },
466            WordStyle::Whitespace(multiline) => {
467                let f = if *multiline {
468                    is_space_char
469                } else {
470                    is_horizontal_space
471                };
472
473                return f(ctx.current) && matches!(ctx.before, Some(c) if !f(c));
474            },
475        }
476    }
477
478    fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool {
479        match self {
480            WordStyle::AlphaNum => {
481                if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
482                    // First character is counted when moving back.
483                    return true;
484                } else if let Some(after) = ctx.after {
485                    let curwc = is_word_char(ctx.current);
486                    let aftwc = is_word_char(after);
487
488                    return curwc && !aftwc;
489                } else {
490                    // Last character is always counted.
491                    return true;
492                }
493            },
494            WordStyle::NonAlphaNum => {
495                if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
496                    // First character is counted when moving back.
497                    return true;
498                } else if let Some(after) = ctx.after {
499                    let curwc = is_word_char(ctx.current);
500                    let aftwc = is_word_char(after);
501
502                    return !curwc && aftwc;
503                } else {
504                    // Last character is always counted.
505                    return true;
506                }
507            },
508            WordStyle::Big => {
509                if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
510                    // First character is counted when moving back.
511                    return true;
512                } else if let Some(after) = ctx.after {
513                    !is_space_char(ctx.current) && is_space_char(after)
514                } else {
515                    // Last character is always a word ending.
516                    return true;
517                }
518            },
519            WordStyle::CharSet(f) => {
520                if let Some(after) = ctx.after {
521                    f(ctx.current) && !f(after)
522                } else {
523                    f(ctx.current)
524                }
525            },
526            WordStyle::FileName => {
527                if let Some(after) = ctx.after {
528                    is_filename_char(ctx.current) && !is_filename_char(after)
529                } else {
530                    is_filename_char(ctx.current)
531                }
532            },
533            WordStyle::FilePath => {
534                if let Some(after) = ctx.after {
535                    is_filepath_char(ctx.current) && !is_filepath_char(after)
536                } else {
537                    is_filepath_char(ctx.current)
538                }
539            },
540            WordStyle::Little => {
541                if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
542                    // First character is counted when moving back.
543                    return true;
544                } else if let Some(after) = ctx.after {
545                    let curwc = is_word_char(ctx.current);
546                    let curkw = is_keyword(ctx.current);
547                    let aftwc = is_word_char(after);
548                    let aftkw = is_keyword(after);
549
550                    return (curwc && aftkw) ||
551                        (curkw && aftwc) ||
552                        (curwc && !aftwc) ||
553                        (curkw && !aftkw);
554                } else {
555                    // Last character is always counted.
556                    return true;
557                }
558            },
559            WordStyle::Number(radix) => {
560                if let Some(after) = ctx.after {
561                    return radix.contains(ctx.current) && !radix.contains(after);
562                } else {
563                    return radix.contains(ctx.current);
564                }
565            },
566            WordStyle::Whitespace(multiline) => {
567                let f = if *multiline {
568                    is_space_char
569                } else {
570                    is_horizontal_space
571                };
572
573                return f(ctx.current) && matches!(ctx.after, Some(c) if !f(c));
574            },
575        }
576    }
577}
578
579/// Specify the base for a number.
580#[derive(Clone, Copy, Debug, Eq, PartialEq)]
581pub enum Radix {
582    /// A base 2 number.
583    Binary,
584
585    /// A base 8 number.
586    Octal,
587
588    /// A base 10 number.
589    Decimal,
590
591    /// A base 16 number.
592    Hexadecimal,
593}
594
595impl Radix {
596    /// Test whether a character is used by this base.
597    pub fn contains(&self, c: char) -> bool {
598        match self {
599            Radix::Binary => c == '0' || c == '1',
600            Radix::Octal => c >= '0' && c <= '7',
601            Radix::Decimal => c.is_ascii_digit(),
602            Radix::Hexadecimal => c.is_ascii_hexdigit(),
603        }
604    }
605}
606
607/// Contextual information given while searching for the boundary of a range.
608pub struct BoundaryTestContext {
609    /// The current candidate character for the object boundary search.
610    pub current: char,
611
612    /// The character that comes before the candidate in the text.
613    pub before: Option<char>,
614
615    /// The character that comes after the candidate in the text.
616    pub after: Option<char>,
617
618    /// The direction the search is moving in.
619    pub dir: MoveDir1D,
620
621    /// Whether we are performing this search as part of a cursor movement.
622    pub motion: bool,
623
624    /// How many boundaries we have left to find.
625    pub count: usize,
626}
627
628/// Trait for types which have simple start and end boundaries within a text document.
629///
630/// Boundaries are searched for a character at a time, with the previous and following character
631/// context provided if available.
632pub trait BoundaryTest {
633    /// Check whether we are at the beginning of the range.
634    fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool;
635
636    /// Check whether we are at the end of the range.
637    fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool;
638
639    /// Check whether we are at the given side of the range.
640    fn is_boundary(&self, terminus: MoveTerminus, ctx: &BoundaryTestContext) -> bool {
641        match terminus {
642            MoveTerminus::Beginning => self.is_boundary_begin(ctx),
643            MoveTerminus::End => self.is_boundary_end(ctx),
644        }
645    }
646}
647
648/// Specify a range within the text around the current cursor position.
649#[derive(Clone, Debug, Eq, PartialEq)]
650#[non_exhaustive]
651pub enum RangeType {
652    /// Select from the beginning to the end of a [word](WordStyle).
653    Word(WordStyle),
654
655    /// Select the whole buffer.
656    Buffer,
657
658    /// Select the current paragraph the cursor is in.
659    Paragraph,
660
661    /// Select the current sentence the cursor is in.
662    Sentence,
663
664    /// Select the current line the cursor is on.
665    Line,
666
667    /// Select the current block specified by the start and end characters.
668    ///
669    /// When done inclusively, the delimiters are included.
670    Bracketed(char, char),
671
672    /// Select the range enclosed by the next item character.
673    ///
674    /// This is the ranged version of [MoveType::ItemMatch].
675    Item,
676
677    /// Select text quoted by [char] around the cursor.
678    ///
679    /// When done inclusively, the quote characters are included.
680    Quote(char),
681
682    /// Select the XML block around the cursor.
683    ///
684    /// When done inclusively, the opening and closing tags are included.
685    XmlTag,
686}
687
688/// Specify a movement away from the current cursor position.
689#[derive(Clone, Debug, Eq, PartialEq)]
690#[non_exhaustive]
691pub enum MoveType {
692    /// Move to a line at a position relative to the buffer.
693    BufferPos(MovePosition),
694
695    /// Move to the column [*n* bytes](Count) into the buffer.
696    BufferByteOffset,
697
698    /// Move to the [*n*<sup>th</sup> line](Count) in the buffer.
699    BufferLineOffset,
700
701    /// Move to the line [*n*%](Count) of the way through the buffer.
702    BufferLinePercent,
703
704    /// Move to the previous or next column [*n* times](Count).
705    ///
706    /// The [bool] parameter indicates whether to cross line boundaries.
707    Column(MoveDir1D, bool),
708
709    /// Move to the final non-blank character [*n* lines](Count) away in [MoveDir1D] direction.
710    FinalNonBlank(MoveDir1D),
711
712    /// Move to the first word [*n* lines](Count) away in [MoveDir1D] direction.
713    FirstWord(MoveDir1D),
714
715    /// Move to the matching character of the next item.
716    ///
717    /// Items are characters like `(`/`)`, `[`/`]`, `{`/`}`, and so on.
718    ItemMatch,
719
720    /// Move [*n* lines](Count) in [MoveDir1D] direction.
721    Line(MoveDir1D),
722
723    /// Move to the [*n*<sup>th</sup>](Count) column in the current line.
724    LineColumnOffset,
725
726    /// Move to the column [*n*%](Count) of the way through the current line.
727    LinePercent,
728
729    /// Move to a column at a position relative to the current line.
730    LinePos(MovePosition),
731
732    /// Move to the beginning of a word [*n* times](Count) in [MoveDir1D] direction.
733    WordBegin(WordStyle, MoveDir1D),
734
735    /// Move to the end of a word [*n* times](Count) in [MoveDir1D] direction.
736    WordEnd(WordStyle, MoveDir1D),
737
738    /// Move to the beginning of a paragraph [*n* times](Count) in [MoveDir1D] direction.
739    ParagraphBegin(MoveDir1D),
740
741    /// Move to the beginning of a sentence [*n* times](Count) in [MoveDir1D] direction.
742    SentenceBegin(MoveDir1D),
743
744    /// Move to the beginning of a section [*n* times](Count) in [MoveDir1D] direction.
745    SectionBegin(MoveDir1D),
746
747    /// Move to the end of a section [*n* times](Count) in [MoveDir1D] direction.
748    SectionEnd(MoveDir1D),
749
750    /// Move to the first word of a screen line [*n* times](Count) away in [MoveDir1D] direction.
751    ScreenFirstWord(MoveDir1D),
752
753    /// Move [*n* screen lines](Count) in [MoveDir1D] direction.
754    ScreenLine(MoveDir1D),
755
756    /// Move to a column at a position relative to the current screen line.
757    ScreenLinePos(MovePosition),
758
759    /// Move to the first word of the line displayed at a position relative to the viewport.
760    ViewportPos(MovePosition),
761}
762
763/// Represent movement along a 1-dimensional line.
764#[derive(Clone, Copy, Debug, Eq, PartialEq)]
765pub enum MoveDir1D {
766    /// Move backwards, or to a previous point.
767    Previous,
768
769    /// Move forwards, or to a following point.
770    Next,
771}
772
773/// Represent movement along the horizontal or vertical axes.
774#[derive(Clone, Copy, Debug, Eq, PartialEq)]
775pub enum MoveDir2D {
776    /// Move leftwards.
777    Left,
778
779    /// Move rightwards.
780    Right,
781
782    /// Move upwards.
783    Up,
784
785    /// Move downwards.
786    Down,
787}
788
789/// Represents the two sides of a range that has no meaningful middle.
790#[derive(Clone, Copy, Debug, Eq, PartialEq)]
791pub enum MoveTerminus {
792    /// The beginning of a range.
793    Beginning,
794
795    /// The end of a range.
796    End,
797}
798
799/// Represent movement to a position along a 1-dimensional line.
800#[derive(Clone, Copy, Debug, Eq, PartialEq)]
801pub enum MovePosition {
802    /// Move to the beginning of some range.
803    Beginning,
804
805    /// Move to the middle of some range.
806    Middle,
807
808    /// Move to the end of some range.
809    End,
810}
811
812/// Represents a modification of a previous [MoveDir1D] movement.
813#[derive(Clone, Copy, Debug, Eq, PartialEq)]
814pub enum MoveDirMod {
815    /// Use the same movement previously used.
816    Same,
817
818    /// Use the opposite of the movement previously used.
819    Flip,
820
821    /// Ignore whatever value was previously used.
822    Exact(MoveDir1D),
823}
824
825/// This represents a selection of a 2-dimensional axis.
826#[derive(Clone, Copy, Debug, Eq, PartialEq)]
827pub enum Axis {
828    /// The horizontal axis.
829    Horizontal,
830
831    /// The vertical axis.
832    Vertical,
833}
834
835impl Axis {
836    /// Rotate a 2-dimensional axis to its opposite.
837    pub fn rotate(&self) -> Axis {
838        match self {
839            Axis::Horizontal => Axis::Vertical,
840            Axis::Vertical => Axis::Horizontal,
841        }
842    }
843}
844
845/// This represents the units used when scrolling.
846#[derive(Clone, Copy, Debug, Eq, PartialEq)]
847pub enum ScrollSize {
848    /// Scroll by number of character cells.
849    Cell,
850
851    /// Scroll by [*n*](Count) times half the page size.
852    HalfPage,
853
854    /// Scroll by [*n*](Count) times the page size.
855    Page,
856}
857
858/// This represents the way in which the viewport should be scrolled.
859#[derive(Clone, Debug, Eq, PartialEq)]
860pub enum ScrollStyle {
861    /// Scroll the viewport in [MoveDir2D] direction by [ScrollSize] units, [*n* times](Count).
862    Direction2D(MoveDir2D, ScrollSize, Count),
863
864    /// Scroll the viewport so that the cursor is placed at [MovePosition] relative to [Axis].
865    CursorPos(MovePosition, Axis),
866
867    /// Scroll the viewport so that the [*n*<sup>th</sup> line](Count) is at [MovePosition] on the screen.
868    LinePos(MovePosition, Count),
869}
870
871/// Place the cursor at a specified position in a visual selection, with the anchor now at the
872/// opposite end.
873#[derive(Clone, Debug, Eq, PartialEq)]
874pub enum SelectionCursorChange {
875    /// Place the cursor in the first line of the selection, in the first column of the selection.
876    Beginning,
877
878    /// Place the cursor in the last line of the selection, in the last column of the selection.
879    End,
880
881    /// Swap the cursor with the anchor of the selection.
882    ///
883    /// [bool] indicates that, when the selection is [BlockWise](TargetShape::BlockWise), the
884    /// cursor should stay on the same line, and only change the column.
885    SwapAnchor(bool),
886}
887
888/// This represents what UI element is targeted during an Action.
889#[derive(Clone, Debug, Eq, PartialEq)]
890pub enum FocusChange {
891    /// Target the currently focused UI element.
892    Current,
893
894    /// Target the [*n*<sup>th</sup> element](Count) from the beginning. The first element is numbered 1.
895    ///
896    /// If the specified *n* is greater than the number of elements, and [bool] is `true`, target
897    /// the last element. Otherwise, do nothing.
898    Offset(Count, bool),
899
900    /// Target the element at [MovePosition] in the element list.
901    Position(MovePosition),
902
903    /// Target the previously focused element.
904    PreviouslyFocused,
905
906    /// Target the element [*n* times](Count) away in [MoveDir1D] direction.
907    ///
908    /// If moving [*n* times](Count) would go past the first or last element, and [bool] is `true`, wrap
909    /// around to the other end of the element list and continue from there. Otherwise, do nothing.
910    Direction1D(MoveDir1D, Count, bool),
911
912    /// Target the element [*n* times](Count) away in [MoveDir2D] direction.
913    Direction2D(MoveDir2D, Count),
914}
915
916/// This represents how to change the size of a window.
917#[derive(Clone, Debug, Eq, PartialEq)]
918pub enum SizeChange<I = Count> {
919    /// Make the window and others along the specified axis the same size.
920    Equal,
921
922    /// Make the window exactly a specific size along the axis.
923    Exact(I),
924
925    /// Decrease the size of the window by a specific amount.
926    Decrease(I),
927
928    /// Increase the size of the window by a specific amount.
929    Increase(I),
930}
931
932/// This represents how to change the indentation of a range.
933#[derive(Clone, Debug, Eq, PartialEq)]
934pub enum IndentChange<I = Count> {
935    /// Automatically determine indentation level.
936    Auto,
937
938    /// Decrease the indentation level of indentation.
939    Decrease(I),
940
941    /// Increase the indentation level of indentation.
942    Increase(I),
943}
944
945/// This represents how to change a number in text.
946#[derive(Clone, Debug, Eq, PartialEq)]
947pub enum NumberChange {
948    /// Decrease the first number in the targeted text by [*n*](Count).
949    Decrease(Count),
950
951    /// Increase the first number in the targeted text by [*n*](Count).
952    Increase(Count),
953}
954
955/// Targets for [WindowAction::Open] and [WindowAction::Switch].
956///
957/// [WindowAction::Open]: crate::WindowAction::Open
958/// [WindowAction::Switch]: crate::WindowAction::Switch
959#[derive(Clone, Debug, Eq, PartialEq)]
960pub enum OpenTarget<W: ApplicationWindowId> {
961    /// An alternate window. This is usually the previous window.
962    Alternate,
963
964    /// An application-specific identifier to switch to.
965    Application(W),
966
967    /// Use the current window as the target.
968    Current,
969
970    /// Use the [word](WordStyle) under the cursor as a target name.
971    Cursor(WordStyle),
972
973    /// An absolute position in a list of targets.
974    List(Count),
975
976    /// A named target (e.g., a filename to open).
977    Name(String),
978
979    /// A window offset from the current one.
980    Offset(MoveDir1D, Count),
981
982    /// Use the selected text as a target name.
983    Selection,
984
985    /// A default window to open when no target has been specified.
986    Unnamed,
987}
988
989/// This represents what tabs are targeted by a tab command.
990#[derive(Clone, Debug, Eq, PartialEq)]
991pub enum TabTarget {
992    /// Close the tab targeted by FocusChange.
993    Single(FocusChange),
994
995    /// Close all tab *except* for the one targeted by FocusChange.
996    AllBut(FocusChange),
997
998    /// Close all tabs.
999    All,
1000}
1001
1002/// This represents what windows are targeted by a window command.
1003#[derive(Clone, Debug, Eq, PartialEq)]
1004pub enum WindowTarget {
1005    /// Close the window targeted by [FocusChange].
1006    Single(FocusChange),
1007
1008    /// Close all windows *except* for the one targeted by [FocusChange].
1009    AllBut(FocusChange),
1010
1011    /// Close all windows.
1012    All,
1013}
1014
1015/// Target cursors in a cursor group.
1016#[derive(Clone, Debug, Eq, PartialEq)]
1017pub enum CursorCloseTarget {
1018    /// Target the cursor group's leader.
1019    Leader,
1020
1021    /// Target the cursor group's followers.
1022    Followers,
1023}
1024
1025/// Ways to combine a newer cursor group with an already existing one.
1026#[derive(Clone, Debug, Eq, PartialEq)]
1027pub enum CursorGroupCombineStyle {
1028    /// Use all of the selections from both groups.
1029    Append,
1030
1031    /// Merge each member with the matching member in the other group.
1032    ///
1033    /// This fails if the groups have a different number of members.
1034    Merge(CursorMergeStyle),
1035
1036    /// Use only the selections in the newer group.
1037    Replace,
1038}
1039
1040impl From<CursorMergeStyle> for CursorGroupCombineStyle {
1041    fn from(style: CursorMergeStyle) -> Self {
1042        CursorGroupCombineStyle::Merge(style)
1043    }
1044}
1045
1046/// Ways to combine two selections.
1047#[derive(Clone, Debug, Eq, PartialEq)]
1048pub enum CursorMergeStyle {
1049    /// Merge the two selections to form one long selection.
1050    Union,
1051
1052    /// Use the intersecting region of the two selections.
1053    Intersect,
1054
1055    /// Select the one where the cursor is furthest in [MoveDir1D] direction.
1056    SelectCursor(MoveDir1D),
1057
1058    /// Select the shorted selection.
1059    SelectShort,
1060
1061    /// Select the longest selection.
1062    SelectLong,
1063}
1064
1065/// This represents how to determine what count argument should be applied to an action.
1066#[derive(Clone, Debug, Eq, PartialEq)]
1067pub enum Count {
1068    /// Use the count provided by the user, or 1 if one was not given.
1069    Contextual,
1070    /// Use the count provided by the user minus 1, or 0 if one was not given.
1071    MinusOne,
1072    /// Ignore the count provided by the user, and use the exact amount specified here.
1073    Exact(usize),
1074}
1075
1076impl From<usize> for Count {
1077    fn from(n: usize) -> Self {
1078        Count::Exact(n)
1079    }
1080}
1081
1082/// Saved cursor positions.
1083#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1084pub enum Mark {
1085    /// The position of the cursor in the current buffer when it last exited.
1086    ///
1087    /// For example, `'"` in Vim.
1088    BufferLastExited,
1089
1090    /// A user-named position in the current buffer.
1091    ///
1092    /// For example, `'[a-z]` in Vim.
1093    BufferNamed(char),
1094
1095    /// The position of the current when the application was previously exited.
1096    ///
1097    /// Index 0 is the cursor position the last time the application exited, 1 the position the
1098    /// second most recent exit, and so on.
1099    ///
1100    /// For example, `'[0-9]` in Vim.
1101    GlobalLastExited(usize),
1102
1103    /// A global, user-named position in some buffer known to the application.
1104    ///
1105    /// For example, `'[A-Z]` in Vim.
1106    GlobalNamed(char),
1107
1108    /// The cursor position where the last change was made.
1109    ///
1110    /// For example, `'.` in Vim.
1111    LastChanged,
1112
1113    /// The cursor position where the last text was inserted.
1114    ///
1115    /// For example, `'^` in Vim.
1116    LastInserted,
1117
1118    /// The cursor position before the latest jump.
1119    ///
1120    /// For example, `''` and `` '` `` in Vim.
1121    LastJump,
1122
1123    /// The position of the beginning of the last text selection.
1124    ///
1125    /// For example, `'<` in Vim.
1126    VisualBegin,
1127
1128    /// The position of the end of the last text selection.
1129    ///
1130    /// For example, `'>` in Vim.
1131    VisualEnd,
1132
1133    /// The position of the beginning of the last yanked text.
1134    ///
1135    /// For example, `'[` in Vim.
1136    LastYankedBegin,
1137
1138    /// The position of the end of the last yanked text.
1139    ///
1140    /// For example, `']` in Vim.
1141    LastYankedEnd,
1142}
1143
1144impl Mark {
1145    /// Indicates whether this is a global mark.
1146    pub fn is_global(&self) -> bool {
1147        match self {
1148            Mark::GlobalNamed(_) => true,
1149            Mark::GlobalLastExited(_) => true,
1150
1151            Mark::BufferLastExited => false,
1152            Mark::BufferNamed(_) => false,
1153            Mark::LastChanged => false,
1154            Mark::LastInserted => false,
1155            Mark::LastJump => false,
1156            Mark::VisualBegin => false,
1157            Mark::VisualEnd => false,
1158            Mark::LastYankedBegin => false,
1159            Mark::LastYankedEnd => false,
1160        }
1161    }
1162}
1163
1164/// A value that may not be known now, but is present in the context.
1165#[derive(Clone, Debug, Default, Eq, PartialEq)]
1166pub enum Specifier<T> {
1167    /// Look for a value of `T` in the [EditContext].
1168    #[default]
1169    Contextual,
1170
1171    /// Use the value of `T` provided here.
1172    Exact(T),
1173}
1174
1175impl<T> From<T> for Specifier<T> {
1176    fn from(v: T) -> Self {
1177        Specifier::Exact(v)
1178    }
1179}
1180
1181bitflags! {
1182    /// These flags are used to specify the behaviour while writing a window.
1183    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
1184    pub struct WriteFlags: u32 {
1185        /// No flags set.
1186        const NONE = 0b00000000;
1187
1188        /// Ignore any issues during closing.
1189        const FORCE = 0b00000001;
1190    }
1191}
1192
1193bitflags! {
1194    /// These flags are used to specify the behaviour while writing a window.
1195    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
1196    pub struct OpenFlags: u32 {
1197        /// No flags set.
1198        const NONE = 0b00000000;
1199
1200        /// Try to ignore any issues during opening.
1201        const FORCE = 0b00000001;
1202
1203        /// Attemp to create the target content if it doesn't already exist.
1204        const CREATE = 0b00000010;
1205    }
1206}
1207
1208bitflags! {
1209    /// These flags are used to specify the behaviour while closing a window.
1210    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
1211    pub struct CloseFlags: u32 {
1212        /// No flags set.
1213        const NONE = 0b00000000;
1214
1215        /// Ignore any issues during closing.
1216        const FORCE = 0b00000001;
1217
1218        /// Write while closing.
1219        const WRITE = 0b00000010;
1220
1221        /// Quit if this is the last window.
1222        const QUIT  = 0b00000100;
1223
1224        /// Write out the window's contents and quit.
1225        const WQ = CloseFlags::WRITE.bits() | CloseFlags::QUIT.bits();
1226
1227        /// Force quit the window.
1228        const FQ = CloseFlags::FORCE.bits() | CloseFlags::QUIT.bits();
1229    }
1230}
1231
1232/// Different ways to expand or trim selections.
1233#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1234#[non_exhaustive]
1235pub enum SelectionBoundary {
1236    /// A selection that starts at the beginning of a line and ends on a newline.
1237    Line,
1238
1239    /// A selection that starts on a non-whitespace character and ends on a non-whitespace
1240    /// character.
1241    NonWhitespace,
1242}
1243
1244impl BoundaryTest for SelectionBoundary {
1245    fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool {
1246        match self {
1247            SelectionBoundary::Line => {
1248                if let Some(before) = ctx.before {
1249                    return before == '\n';
1250                } else {
1251                    return true;
1252                }
1253            },
1254            SelectionBoundary::NonWhitespace => {
1255                return !is_space_char(ctx.current);
1256            },
1257        }
1258    }
1259
1260    fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool {
1261        match self {
1262            SelectionBoundary::Line => ctx.current == '\n' || ctx.after.is_none(),
1263            SelectionBoundary::NonWhitespace => {
1264                return !is_space_char(ctx.current);
1265            },
1266        }
1267    }
1268}
1269
1270/// Different ways to split existing selections into new ones.
1271#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1272#[non_exhaustive]
1273pub enum SelectionSplitStyle {
1274    /// Split a selection into two [TargetShape::CharWise] selections, one at the current cursor
1275    /// position, and the other at the anchor.
1276    Anchor,
1277
1278    /// Split a selection at each line boundary it contains.
1279    Lines,
1280
1281    /// Split a selection into [TargetShape::CharWise] parts based on the regular expression
1282    /// stored in the register for [CommandType::Search].
1283    ///
1284    /// When the [bool] argument is `false`, then text matching the regular expression will be
1285    /// selected.
1286    ///
1287    /// When the [bool] argument is `true`, then text matching the regular expression will be
1288    /// removed from the selections.
1289    Regex(bool),
1290}
1291
1292/// Different ways to change the boundaries of a visual selection.
1293#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1294#[non_exhaustive]
1295pub enum SelectionResizeStyle {
1296    /// Extend (or possibly shrink) the selection by moving the cursor.
1297    ///
1298    /// When extending with [EditTarget::Range], this may also move the anchor to fully encompass
1299    /// the [RangeType].
1300    Extend,
1301
1302    /// Interpret the [EditTarget] as the bounds of a text object, and select it.
1303    Object,
1304
1305    /// Move the anchor to the current cursor position and create a new selection from there.
1306    Restart,
1307}
1308
1309/// When focusing on the command bar, this is the type of command that should be submitted.
1310#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1311pub enum CommandType {
1312    /// Prompt the user for a command.
1313    Command,
1314
1315    /// Prompt the user for a search query.
1316    Search,
1317}
1318
1319/// This specifies which list of cursors to use when jumping, the change list or the jump list.
1320#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1321pub enum PositionList {
1322    /// The change list contains positions where changes were previously made.
1323    ChangeList,
1324
1325    /// The jump list contains positions where the cursor was placed before jumping to a new
1326    /// location in the document.
1327    JumpList,
1328}
1329
1330/// This specifies the behaviour of entering and backspacing over characters.
1331#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1332pub enum InsertStyle {
1333    /// This specifies that typed characters should leave existing ones as is, and backspacing
1334    /// should remove characters.
1335    Insert,
1336
1337    /// This specifies that typed characters should replace existing ones, and backspacing should
1338    /// restore any overwritten characters.
1339    Replace,
1340}
1341
1342/// A character.
1343#[derive(Clone, Debug, Eq, PartialEq)]
1344pub enum Char {
1345    /// An exact character.
1346    Single(char),
1347    /// A digraph sequence.
1348    Digraph(char, char),
1349    /// A terminal control sequence.
1350    CtrlSeq(String),
1351    /// Copy a character from the same column in the previous or next line.
1352    CopyLine(MoveDir1D),
1353}
1354
1355impl From<char> for Char {
1356    fn from(c: char) -> Self {
1357        Char::Single(c)
1358    }
1359}
1360
1361/// Locations for temporarily storing text shared between buffers.
1362#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1363#[non_exhaustive]
1364pub enum Register {
1365    /// The default register.
1366    ///
1367    /// For example, `""` in Vim.
1368    Unnamed,
1369
1370    /// The default macro register.
1371    ///
1372    /// For example, `"@` in Kakoune.
1373    UnnamedMacro,
1374
1375    /// The default cursor group register.
1376    ///
1377    ///
1378    /// For example, `"^` in Kakoune.
1379    UnnamedCursorGroup,
1380
1381    /// Recently deleted text.
1382    ///
1383    /// For example, `"[1-9]` in Vim.
1384    RecentlyDeleted(usize),
1385
1386    /// Most recently deleted text that was shorted than a line.
1387    ///
1388    /// For example, `"-` in Vim.
1389    SmallDelete,
1390
1391    /// A register containing the last inserted text.
1392    ///
1393    /// For example, `".` in Vim.
1394    LastInserted,
1395
1396    /// A register containing the last value entered for a [CommandType].
1397    ///
1398    /// For example, `":` and `"/` in Vim.
1399    LastCommand(CommandType),
1400
1401    /// A register containing the last copied text.
1402    ///
1403    /// For eample, `"0` in Vim.
1404    LastYanked,
1405
1406    /// A register named by `char`.
1407    ///
1408    /// The index of the most recent deletion is 0, the second most recent deletion is 1, and so
1409    /// on.
1410    ///
1411    /// For example, `"[a-zA-Z]` in Vim.
1412    Named(char),
1413
1414    /// A read-only register containing the alternate buffer name.
1415    ///
1416    /// For example, `"#` in Vim.
1417    AltBufName,
1418
1419    /// A read-only register containing the current buffer name.
1420    ///
1421    /// For example, `"%` in Vim.
1422    CurBufName,
1423
1424    /// A register that discards all content written to it.
1425    ///
1426    /// For example, `"_` in Vim.
1427    Blackhole,
1428
1429    /// A register representing the windowing environment's most recently selected text.
1430    ///
1431    /// For example, `"*` in Vim, or what clicking the mouse's middle button pastes in X and
1432    /// Wayland.
1433    SelectionPrimary,
1434
1435    /// A register representing the windowing environment's most recently copied text.
1436    ///
1437    /// For example, `"+` in Vim, or what the keyboard shortcut pastes in X and Wayland.
1438    SelectionClipboard,
1439}
1440
1441impl Register {
1442    /// Indicates whether a given register is allowed to store cursor groups.
1443    pub fn is_cursor_storage(&self) -> bool {
1444        match self {
1445            Register::Named(_) => true,
1446            Register::Blackhole => true,
1447            Register::UnnamedCursorGroup => true,
1448
1449            Register::Unnamed => false,
1450            Register::UnnamedMacro => false,
1451            Register::RecentlyDeleted(_) => false,
1452            Register::SmallDelete => false,
1453            Register::LastCommand(_) => false,
1454            Register::LastInserted => false,
1455            Register::LastYanked => false,
1456            Register::AltBufName => false,
1457            Register::CurBufName => false,
1458            Register::SelectionPrimary => false,
1459            Register::SelectionClipboard => false,
1460        }
1461    }
1462}
1463
1464/// This specifies either the shape of a visual selection, or a forced motion.
1465#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1466pub enum TargetShape {
1467    /// A series of characters.
1468    ///
1469    /// During a selection, the two points indicate the start and end columns.
1470    CharWise,
1471
1472    /// A series of lines.
1473    ///
1474    /// During a selection, the two points indicate the start and end lines.
1475    LineWise,
1476
1477    /// A block of characters.
1478    ///
1479    /// During a selection, the two points indicate opposite corners.
1480    BlockWise,
1481}
1482
1483bitflags! {
1484    /// Bitmask that specifies what shapes are targeted by an action.
1485    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
1486    pub struct TargetShapeFilter: u32 {
1487        /// Match no shapes.
1488        const NONE = 0b00000000;
1489
1490        /// Match all shapes.
1491        const ALL = 0b00000111;
1492
1493        /// Match [TargetShape::CharWise].
1494        const CHAR = 0b00000001;
1495
1496        /// Match [TargetShape::LineWise].
1497        const LINE = 0b00000010;
1498
1499        /// Match [TargetShape::BlockWise].
1500        const BLOCK = 0b00000100;
1501    }
1502}
1503
1504impl TargetShapeFilter {
1505    /// Check whether this filter applies to a given [TargetShape].
1506    pub fn matches(&self, shape: &TargetShape) -> bool {
1507        match shape {
1508            TargetShape::CharWise => self.contains(TargetShapeFilter::CHAR),
1509            TargetShape::LineWise => self.contains(TargetShapeFilter::LINE),
1510            TargetShape::BlockWise => self.contains(TargetShapeFilter::BLOCK),
1511        }
1512    }
1513}
1514
1515/// Methods for determining the start and end of a [RangeSpec].
1516#[derive(Clone, Debug, Eq, PartialEq)]
1517pub enum RangeEndingType {
1518    /// A specific line number.
1519    Absolute(Count),
1520
1521    /// All lines.
1522    All,
1523
1524    /// The current line.
1525    Current,
1526
1527    /// The last line.
1528    Last,
1529
1530    /// The position of a given [Mark].
1531    Mark(Specifier<Mark>),
1532
1533    /// The line matching a search using the last value of [CommandType::Search].
1534    Search(MoveDir1D),
1535
1536    /// Perform a search using the last substitution pattern.
1537    SubPatSearch(MoveDir1D),
1538
1539    /// No line was specified.
1540    Unspecified,
1541}
1542
1543/// Modifier to a range ending.
1544#[non_exhaustive]
1545#[derive(Clone, Debug, Eq, PartialEq)]
1546pub enum RangeEndingModifier {
1547    /// Offset the end of a range by [*n*](Count) lines.
1548    Offset(MoveDir1D, Count),
1549}
1550
1551/// One of the sides of a range.
1552#[derive(Clone, Debug, Eq, PartialEq)]
1553pub struct RangeEnding(pub RangeEndingType, pub Vec<RangeEndingModifier>);
1554
1555/// Position to begin a search in a range.
1556#[derive(Clone, Debug, Eq, PartialEq)]
1557pub enum RangeSearchInit {
1558    /// Start from current cursor position.
1559    Cursor,
1560
1561    /// Start from the beginning of the range.
1562    Start,
1563}
1564
1565/// A range specification.
1566#[derive(Clone, Debug, Eq, PartialEq)]
1567pub enum RangeSpec {
1568    /// A range specification where only one end of the range was given.
1569    Single(RangeEnding),
1570
1571    /// A range specification where both ends of the range were given.
1572    Double(RangeEnding, RangeEnding, RangeSearchInit),
1573}
1574
1575/// Trait for objects that allow toggling line wrapping.
1576pub trait Wrappable {
1577    /// Set whether or not displayed lines should be wrapped.
1578    fn set_wrap(&mut self, wrap: bool);
1579}
1580
1581/// Information about what portion of a buffer is being displayed in a window.
1582pub struct ViewportContext<Cursor> {
1583    /// The line and column offset into the buffer shown at the upper-left hand corner of the
1584    /// window.
1585    pub corner: Cursor,
1586
1587    /// Dimensions of the window.
1588    pub dimensions: (usize, usize),
1589
1590    /// Whether or not displayed lines are being wrapped.
1591    pub wrap: bool,
1592}
1593
1594impl<Cursor: Default> ViewportContext<Cursor> {
1595    /// Create a new context for describing a viewport.
1596    pub fn new() -> Self {
1597        ViewportContext {
1598            corner: Cursor::default(),
1599            dimensions: (0, 0),
1600            wrap: false,
1601        }
1602    }
1603
1604    /// Get the viewport height.
1605    pub fn get_height(&self) -> usize {
1606        self.dimensions.1
1607    }
1608
1609    /// Get the viewport width.
1610    pub fn get_width(&self) -> usize {
1611        self.dimensions.0
1612    }
1613}
1614
1615impl<Cursor: Default> Default for ViewportContext<Cursor> {
1616    fn default() -> Self {
1617        ViewportContext::new()
1618    }
1619}
1620
1621impl<Cursor: Clone> Clone for ViewportContext<Cursor> {
1622    fn clone(&self) -> Self {
1623        ViewportContext {
1624            corner: self.corner.clone(),
1625            dimensions: self.dimensions,
1626            wrap: self.wrap,
1627        }
1628    }
1629}
1630
1631impl<Cursor: Wrappable> Wrappable for ViewportContext<Cursor> {
1632    fn set_wrap(&mut self, wrap: bool) {
1633        self.wrap = wrap;
1634        self.corner.set_wrap(wrap);
1635    }
1636}
1637
1638/// This context object wraps information used when calculating what text covered by cursor
1639/// movements.
1640pub struct CursorMovementsContext<'a, Cursor> {
1641    /// What operation this movement is being done as part of.
1642    ///
1643    /// Certain movements, like [MoveType::WordBegin], behave different depending on the action.
1644    pub action: &'a EditAction,
1645
1646    /// Information about the user's view of the text, since this impacts movements that rely on
1647    /// how the text is displayed, such as [MoveType::ScreenLine].
1648    pub view: &'a ViewportContext<Cursor>,
1649
1650    /// The editing context contains information about the current [InsertStyle], as well as the
1651    /// user-supplied [Count].
1652    pub context: &'a EditContext,
1653}
1654
1655/// Trait for objects capable of calculating contextual offsets from a cursor.
1656pub trait CursorMovements<Cursor> {
1657    /// Calculate the position of the first word on the line of the provided cursor.
1658    fn first_word(&self, cursor: &Cursor, ctx: &CursorMovementsContext<'_, Cursor>) -> Cursor;
1659
1660    /// Calculate the position of the cursor after performing a movement.
1661    fn movement(
1662        &self,
1663        cursor: &Cursor,
1664        movement: &MoveType,
1665        count: &Count,
1666        ctx: &CursorMovementsContext<'_, Cursor>,
1667    ) -> Option<Cursor>;
1668
1669    /// Calculate a cursor range from the given cursor to the location after performing the
1670    /// given movement.
1671    fn range_of_movement(
1672        &self,
1673        cursor: &Cursor,
1674        movement: &MoveType,
1675        count: &Count,
1676        ctx: &CursorMovementsContext<'_, Cursor>,
1677    ) -> Option<EditRange<Cursor>>;
1678
1679    /// Calculate a cursor range based on a given cursor position and a [RangeType].
1680    fn range(
1681        &self,
1682        cursor: &Cursor,
1683        range: &RangeType,
1684        inclusive: bool,
1685        count: &Count,
1686        ctx: &CursorMovementsContext<'_, Cursor>,
1687    ) -> Option<EditRange<Cursor>>;
1688}
1689
1690/// Trait for objects capable of searching text.
1691pub trait CursorSearch<Cursor> {
1692    /// Search for a specific character.
1693    fn find_char(
1694        &self,
1695        cursor: &Cursor,
1696        inclusive: bool,
1697        dir: MoveDir1D,
1698        multiline: bool,
1699        needle: char,
1700        count: usize,
1701    ) -> Option<Cursor>;
1702
1703    /// Find matches for a regular expression within a range.
1704    fn find_matches(&self, start: &Cursor, end: &Cursor, needle: &Regex) -> Vec<EditRange<Cursor>>;
1705
1706    /// Search for a regular expression.
1707    fn find_regex(
1708        &self,
1709        cursor: &Cursor,
1710        dir: MoveDir1D,
1711        needle: &Regex,
1712        count: usize,
1713    ) -> Option<EditRange<Cursor>>;
1714}
1715
1716/// Trait for directions capable of being flipped.
1717pub trait Flip {
1718    /// Return the flipped representation of the value.
1719    fn flip(&self) -> Self;
1720}
1721
1722impl Flip for MoveDir1D {
1723    fn flip(&self) -> MoveDir1D {
1724        match self {
1725            MoveDir1D::Previous => MoveDir1D::Next,
1726            MoveDir1D::Next => MoveDir1D::Previous,
1727        }
1728    }
1729}
1730
1731impl Flip for MoveDir2D {
1732    fn flip(&self) -> MoveDir2D {
1733        match self {
1734            MoveDir2D::Left => MoveDir2D::Right,
1735            MoveDir2D::Right => MoveDir2D::Left,
1736            MoveDir2D::Up => MoveDir2D::Down,
1737            MoveDir2D::Down => MoveDir2D::Up,
1738        }
1739    }
1740}
1741
1742impl MoveDir2D {
1743    /// Returns the [Axis] that the direction moves along.
1744    pub fn axis(&self) -> Axis {
1745        match self {
1746            MoveDir2D::Left => Axis::Horizontal,
1747            MoveDir2D::Right => Axis::Horizontal,
1748            MoveDir2D::Up => Axis::Vertical,
1749            MoveDir2D::Down => Axis::Vertical,
1750        }
1751    }
1752}
1753
1754impl std::ops::Not for InsertStyle {
1755    type Output = Self;
1756
1757    fn not(self) -> Self::Output {
1758        match self {
1759            InsertStyle::Insert => InsertStyle::Replace,
1760            InsertStyle::Replace => InsertStyle::Insert,
1761        }
1762    }
1763}
1764
1765impl MoveType {
1766    /// Returns `true` if this is an inclusive motion.
1767    pub fn is_inclusive_motion(&self) -> bool {
1768        match self {
1769            MoveType::BufferPos(_) => true,
1770            MoveType::FinalNonBlank(_) => true,
1771            MoveType::ItemMatch => true,
1772            MoveType::LineColumnOffset => true,
1773            MoveType::WordEnd(_, _) => true,
1774
1775            MoveType::BufferByteOffset => false,
1776            MoveType::BufferLineOffset => false,
1777            MoveType::BufferLinePercent => false,
1778            MoveType::Column(_, _) => false,
1779            MoveType::FirstWord(_) => false,
1780            MoveType::Line(_) => false,
1781            MoveType::LinePercent => false,
1782            MoveType::LinePos(_) => false,
1783            MoveType::ParagraphBegin(_) => false,
1784            MoveType::ScreenFirstWord(_) => false,
1785            MoveType::ScreenLine(_) => false,
1786            MoveType::ScreenLinePos(_) => false,
1787            MoveType::ViewportPos(_) => false,
1788            MoveType::SectionBegin(_) => false,
1789            MoveType::SectionEnd(_) => false,
1790            MoveType::SentenceBegin(_) => false,
1791            MoveType::WordBegin(_, _) => false,
1792        }
1793    }
1794
1795    /// Returns `true` if this is a motion that causes cursor positions to be saved to
1796    /// [PositionList::JumpList].
1797    pub fn is_jumping(&self) -> bool {
1798        match self {
1799            MoveType::BufferByteOffset => true,
1800            MoveType::BufferLineOffset => true,
1801            MoveType::BufferLinePercent => true,
1802            MoveType::BufferPos(_) => true,
1803            MoveType::ItemMatch => true,
1804            MoveType::ParagraphBegin(_) => true,
1805            MoveType::ViewportPos(_) => true,
1806            MoveType::SectionBegin(_) => true,
1807            MoveType::SentenceBegin(_) => true,
1808
1809            MoveType::Column(_, _) => false,
1810            MoveType::FinalNonBlank(_) => false,
1811            MoveType::FirstWord(_) => false,
1812            MoveType::LineColumnOffset => false,
1813            MoveType::Line(_) => false,
1814            MoveType::LinePercent => false,
1815            MoveType::LinePos(_) => false,
1816            MoveType::ScreenFirstWord(_) => false,
1817            MoveType::ScreenLine(_) => false,
1818            MoveType::ScreenLinePos(_) => false,
1819            MoveType::SectionEnd(_) => false,
1820            MoveType::WordBegin(_, _) => false,
1821            MoveType::WordEnd(_, _) => false,
1822        }
1823    }
1824
1825    /// Returns the shape of the text selected by this movement when editing.
1826    pub fn shape(&self) -> TargetShape {
1827        match self {
1828            MoveType::BufferLineOffset => TargetShape::LineWise,
1829            MoveType::BufferLinePercent => TargetShape::LineWise,
1830            MoveType::BufferPos(_) => TargetShape::LineWise,
1831            MoveType::FirstWord(_) => TargetShape::LineWise,
1832            MoveType::Line(_) => TargetShape::LineWise,
1833            MoveType::ViewportPos(_) => TargetShape::LineWise,
1834            MoveType::SectionBegin(_) => TargetShape::LineWise,
1835            MoveType::SectionEnd(_) => TargetShape::LineWise,
1836
1837            MoveType::BufferByteOffset => TargetShape::CharWise,
1838            MoveType::Column(_, _) => TargetShape::CharWise,
1839            MoveType::FinalNonBlank(_) => TargetShape::CharWise,
1840            MoveType::ItemMatch => TargetShape::CharWise,
1841            MoveType::LineColumnOffset => TargetShape::CharWise,
1842            MoveType::LinePercent => TargetShape::CharWise,
1843            MoveType::LinePos(_) => TargetShape::CharWise,
1844            MoveType::ParagraphBegin(_) => TargetShape::CharWise,
1845            MoveType::ScreenFirstWord(_) => TargetShape::CharWise,
1846            MoveType::ScreenLinePos(_) => TargetShape::CharWise,
1847            MoveType::ScreenLine(_) => TargetShape::CharWise,
1848            MoveType::SentenceBegin(_) => TargetShape::CharWise,
1849            MoveType::WordBegin(_, _) => TargetShape::CharWise,
1850            MoveType::WordEnd(_, _) => TargetShape::CharWise,
1851        }
1852    }
1853}
1854
1855impl MoveDirMod {
1856    /// Modify a given direction.
1857    pub fn resolve(&self, dir: &MoveDir1D) -> MoveDir1D {
1858        match self {
1859            MoveDirMod::Same => *dir,
1860            MoveDirMod::Flip => dir.flip(),
1861            MoveDirMod::Exact(exact) => *exact,
1862        }
1863    }
1864}
1865
1866impl From<MoveDir1D> for MoveDirMod {
1867    fn from(dir: MoveDir1D) -> Self {
1868        MoveDirMod::Exact(dir)
1869    }
1870}
1871
1872/// Information to show the user at the bottom of the screen after an action.
1873#[derive(Clone, Debug, Eq, PartialEq)]
1874pub enum InfoMessage {
1875    /// Print a simple, informational message on the status line.
1876    Message(String),
1877
1878    /// Use an interactive pager to show the user some information.
1879    ///
1880    /// If you're using [keybindings], then you can handle this using [Pager] and
1881    /// [BindingMachine::run_dialog].
1882    ///
1883    /// [Pager]: keybindings::dialog::Pager
1884    /// [BindingMachine::run_dialog]: keybindings::BindingMachine::run_dialog
1885    Pager(String),
1886}
1887
1888impl Display for InfoMessage {
1889    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1890        match self {
1891            InfoMessage::Message(s) | InfoMessage::Pager(s) => write!(f, "{}", s),
1892        }
1893    }
1894}
1895
1896impl From<&str> for InfoMessage {
1897    fn from(msg: &str) -> Self {
1898        InfoMessage::from(msg.to_string())
1899    }
1900}
1901
1902impl From<String> for InfoMessage {
1903    fn from(msg: String) -> Self {
1904        InfoMessage::Message(msg)
1905    }
1906}
1907
1908/// An optional, information message provided during editing.
1909pub type EditInfo = Option<InfoMessage>;