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