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