1pub mod application;
30pub mod context;
31pub mod prelude;
32pub mod util;
33
34use self::application::*;
35use self::context::{EditContext, Resolve};
36use self::prelude::*;
37use keybindings::SequenceStatus;
38
39#[derive(Clone, Debug, Default, Eq, PartialEq)]
41pub enum EditAction {
42 #[default]
48 Motion,
49
50 Delete,
52
53 Yank,
55
56 Replace(bool),
60
61 Format,
63
64 ChangeNumber(NumberChange, bool),
68
69 Join(JoinStyle),
73
74 Indent(IndentChange),
76
77 ChangeCase(Case),
79}
80
81impl EditAction {
82 pub fn is_readonly(&self) -> bool {
84 match self {
85 EditAction::Motion => true,
86 EditAction::Yank => true,
87
88 EditAction::ChangeCase(_) => false,
89 EditAction::ChangeNumber(_, _) => false,
90 EditAction::Delete => false,
91 EditAction::Format => false,
92 EditAction::Indent(_) => false,
93 EditAction::Join(_) => false,
94 EditAction::Replace(_) => false,
95 }
96 }
97
98 pub fn is_motion(&self) -> bool {
100 matches!(self, EditAction::Motion)
101 }
102
103 pub fn is_switchable(&self, _: &EditContext) -> bool {
106 self.is_motion()
107 }
108}
109
110#[derive(Clone, Debug, Eq, PartialEq)]
112#[non_exhaustive]
113pub enum SelectionAction {
114 Duplicate(MoveDir1D, Count),
119
120 CursorSet(SelectionCursorChange),
122
123 Expand(SelectionBoundary, TargetShapeFilter),
129
130 Filter(bool),
135
136 Join,
138
139 Resize(SelectionResizeStyle, EditTarget),
145
146 Split(SelectionSplitStyle, TargetShapeFilter),
150
151 Trim(SelectionBoundary, TargetShapeFilter),
157}
158
159#[derive(Clone, Debug, Eq, PartialEq)]
161#[non_exhaustive]
162pub enum InsertTextAction {
163 OpenLine(TargetShape, MoveDir1D, Count),
165
166 Paste(PasteStyle, Count),
168
169 Transcribe(String, MoveDir1D, Count),
171
172 Type(Specifier<Char>, MoveDir1D, Count),
174}
175
176#[derive(Clone, Debug, Eq, PartialEq)]
178pub enum HistoryAction {
179 Checkpoint,
181
182 Redo(Count),
184
185 Undo(Count),
187}
188
189impl HistoryAction {
190 pub fn is_readonly(&self) -> bool {
192 match self {
193 HistoryAction::Redo(_) => false,
194 HistoryAction::Undo(_) => false,
195 HistoryAction::Checkpoint => true,
196 }
197 }
198}
199
200#[derive(Clone, Debug, Eq, PartialEq)]
202#[non_exhaustive]
203pub enum CursorAction {
204 Close(CursorCloseTarget),
206
207 Restore(CursorGroupCombineStyle),
212
213 Rotate(MoveDir1D, Count),
215
216 Save(CursorGroupCombineStyle),
221
222 Split(Count),
224}
225
226impl CursorAction {
227 pub fn is_switchable(&self, _: &EditContext) -> bool {
230 match self {
231 CursorAction::Restore(_) => true,
232
233 CursorAction::Close(_) => false,
234 CursorAction::Rotate(..) => false,
235 CursorAction::Save(_) => false,
236 CursorAction::Split(_) => false,
237 }
238 }
239}
240
241#[derive(Clone, Debug, Eq, PartialEq)]
243#[non_exhaustive]
244pub enum CommandAction {
245 Run(String),
249
250 Execute(Count),
252}
253
254#[derive(Clone, Debug, Eq, PartialEq)]
256pub enum CommandBarAction<I: ApplicationInfo> {
257 Focus(String, CommandType, Box<Action<I>>),
259
260 Unfocus,
262}
263
264#[derive(Clone, Debug, Eq, PartialEq)]
266pub enum PromptAction {
267 Abort(bool),
272
273 Submit,
275
276 Recall(MoveDir1D, Count, bool),
281}
282
283#[derive(Clone, Debug, Eq, PartialEq)]
285#[non_exhaustive]
286pub enum MacroAction {
287 Execute(Count),
291
292 Run(String, Count),
294
295 Repeat(Count),
297
298 ToggleRecording,
300}
301
302#[derive(Clone, Debug, Eq, PartialEq)]
304#[non_exhaustive]
305pub enum TabAction<I: ApplicationInfo> {
306 Close(TabTarget, CloseFlags),
308
309 Extract(FocusChange, MoveDir1D),
318
319 Focus(FocusChange),
321
322 Move(FocusChange),
324
325 Open(OpenTarget<I::WindowId>, FocusChange),
327}
328
329#[derive(Clone, Debug, Eq, PartialEq)]
331#[non_exhaustive]
332pub enum WindowAction<I: ApplicationInfo> {
333 Close(WindowTarget, CloseFlags),
335
336 Exchange(FocusChange),
338
339 Focus(FocusChange),
341
342 MoveSide(MoveDir2D),
344
345 Open(OpenTarget<I::WindowId>, Axis, MoveDir1D, Count),
348
349 Rotate(MoveDir1D),
351
352 Split(OpenTarget<I::WindowId>, Axis, MoveDir1D, Count),
355
356 Switch(OpenTarget<I::WindowId>),
361
362 ClearSizes,
365
366 Resize(FocusChange, Axis, SizeChange),
368
369 Write(WindowTarget, Option<String>, WriteFlags),
371
372 ZoomToggle,
375}
376
377#[derive(Clone, Debug, Eq, PartialEq)]
379#[non_exhaustive]
380pub enum EditorAction {
381 Complete(CompletionType, CompletionSelection, CompletionDisplay),
383
384 Cursor(CursorAction),
386
387 Edit(Specifier<EditAction>, EditTarget),
389
390 History(HistoryAction),
392
393 InsertText(InsertTextAction),
395
396 Mark(Specifier<Mark>),
398
399 Selection(SelectionAction),
401}
402
403impl EditorAction {
404 pub fn is_readonly(&self, ctx: &EditContext) -> bool {
406 match self {
407 EditorAction::Complete(_, _, _) => false,
408 EditorAction::History(act) => act.is_readonly(),
409 EditorAction::InsertText(_) => false,
410
411 EditorAction::Cursor(_) => true,
412 EditorAction::Mark(_) => true,
413 EditorAction::Selection(_) => true,
414
415 EditorAction::Edit(act, _) => ctx.resolve(act).is_readonly(),
416 }
417 }
418
419 pub fn is_edit_sequence(&self, motion: SequenceStatus, ctx: &EditContext) -> SequenceStatus {
423 match self {
424 EditorAction::History(_) => SequenceStatus::Break,
425 EditorAction::Mark(_) => SequenceStatus::Break,
426 EditorAction::InsertText(_) => SequenceStatus::Track,
427 EditorAction::Cursor(_) => SequenceStatus::Track,
428 EditorAction::Selection(_) => SequenceStatus::Track,
429 EditorAction::Complete(_, _, _) => SequenceStatus::Track,
430 EditorAction::Edit(act, _) => {
431 match ctx.resolve(act) {
432 EditAction::Motion => motion,
433 EditAction::Yank => SequenceStatus::Ignore,
434 _ => SequenceStatus::Track,
435 }
436 },
437 }
438 }
439
440 pub fn is_last_action(&self, _: &EditContext) -> SequenceStatus {
442 match self {
443 EditorAction::History(HistoryAction::Checkpoint) => SequenceStatus::Ignore,
444 EditorAction::History(HistoryAction::Undo(_)) => SequenceStatus::Atom,
445 EditorAction::History(HistoryAction::Redo(_)) => SequenceStatus::Atom,
446
447 EditorAction::Complete(_, _, _) => SequenceStatus::Atom,
448 EditorAction::Cursor(_) => SequenceStatus::Atom,
449 EditorAction::Edit(_, _) => SequenceStatus::Atom,
450 EditorAction::InsertText(_) => SequenceStatus::Atom,
451 EditorAction::Mark(_) => SequenceStatus::Atom,
452 EditorAction::Selection(_) => SequenceStatus::Atom,
453 }
454 }
455
456 pub fn is_last_selection(&self, ctx: &EditContext) -> SequenceStatus {
458 match self {
459 EditorAction::History(_) => SequenceStatus::Ignore,
460 EditorAction::Mark(_) => SequenceStatus::Ignore,
461 EditorAction::InsertText(_) => SequenceStatus::Ignore,
462 EditorAction::Cursor(_) => SequenceStatus::Ignore,
463 EditorAction::Complete(_, _, _) => SequenceStatus::Ignore,
464
465 EditorAction::Selection(SelectionAction::Resize(_, _)) => SequenceStatus::Track,
466 EditorAction::Selection(_) => SequenceStatus::Ignore,
467
468 EditorAction::Edit(act, _) => {
469 if let EditAction::Motion = ctx.resolve(act) {
470 if ctx.get_target_shape().is_some() {
471 SequenceStatus::Restart
472 } else {
473 SequenceStatus::Ignore
474 }
475 } else {
476 SequenceStatus::Ignore
477 }
478 },
479 }
480 }
481
482 pub fn is_switchable(&self, ctx: &EditContext) -> bool {
484 match self {
485 EditorAction::Cursor(act) => act.is_switchable(ctx),
486 EditorAction::Edit(act, _) => ctx.resolve(act).is_switchable(ctx),
487 EditorAction::Complete(_, _, _) => false,
488 EditorAction::History(_) => false,
489 EditorAction::InsertText(_) => false,
490 EditorAction::Mark(_) => false,
491 EditorAction::Selection(_) => false,
492 }
493 }
494}
495
496impl From<CursorAction> for EditorAction {
497 fn from(act: CursorAction) -> Self {
498 EditorAction::Cursor(act)
499 }
500}
501
502impl From<HistoryAction> for EditorAction {
503 fn from(act: HistoryAction) -> Self {
504 EditorAction::History(act)
505 }
506}
507
508impl From<InsertTextAction> for EditorAction {
509 fn from(act: InsertTextAction) -> Self {
510 EditorAction::InsertText(act)
511 }
512}
513
514impl From<SelectionAction> for EditorAction {
515 fn from(act: SelectionAction) -> Self {
516 EditorAction::Selection(act)
517 }
518}
519
520#[derive(Clone, Debug, Eq, PartialEq)]
522#[non_exhaustive]
523pub enum Action<I: ApplicationInfo = EmptyInfo> {
524 NoOp,
526
527 Editor(EditorAction),
529
530 Macro(MacroAction),
532
533 Jump(PositionList, MoveDir1D, Count),
538
539 Repeat(RepeatType),
541
542 Scroll(ScrollStyle),
544
545 KeywordLookup,
547
548 RedrawScreen,
550
551 ShowInfoMessage(InfoMessage),
553
554 Suspend,
556
557 Search(MoveDirMod, Count),
559
560 Command(CommandAction),
562
563 CommandBar(CommandBarAction<I>),
565
566 Prompt(PromptAction),
568
569 Tab(TabAction<I>),
571
572 Window(WindowAction<I>),
574
575 Application(I::Action),
577}
578
579impl<I: ApplicationInfo> Action<I> {
580 pub fn is_edit_sequence(&self, motion: SequenceStatus, ctx: &EditContext) -> SequenceStatus {
584 match self {
585 Action::Repeat(_) => SequenceStatus::Ignore,
586
587 Action::Application(act) => act.is_edit_sequence(ctx),
588 Action::Editor(act) => act.is_edit_sequence(motion, ctx),
589
590 Action::Command(_) => SequenceStatus::Break,
591 Action::CommandBar(_) => SequenceStatus::Break,
592 Action::Jump(_, _, _) => SequenceStatus::Break,
593 Action::Macro(_) => SequenceStatus::Break,
594 Action::Prompt(_) => SequenceStatus::Break,
595 Action::Tab(_) => SequenceStatus::Break,
596 Action::Window(_) => SequenceStatus::Break,
597
598 Action::KeywordLookup => SequenceStatus::Ignore,
599 Action::NoOp => SequenceStatus::Ignore,
600 Action::RedrawScreen => SequenceStatus::Ignore,
601 Action::Scroll(_) => SequenceStatus::Ignore,
602 Action::Search(_, _) => SequenceStatus::Ignore,
603 Action::ShowInfoMessage(_) => SequenceStatus::Ignore,
604 Action::Suspend => SequenceStatus::Ignore,
605 }
606 }
607
608 pub fn is_last_action(&self, ctx: &EditContext) -> SequenceStatus {
610 match self {
611 Action::Repeat(RepeatType::EditSequence) => SequenceStatus::Atom,
612 Action::Repeat(RepeatType::LastAction) => SequenceStatus::Ignore,
613 Action::Repeat(RepeatType::LastSelection) => SequenceStatus::Atom,
614
615 Action::Application(act) => act.is_last_action(ctx),
616 Action::Editor(act) => act.is_last_action(ctx),
617
618 Action::Command(_) => SequenceStatus::Atom,
619 Action::CommandBar(_) => SequenceStatus::Atom,
620 Action::Jump(_, _, _) => SequenceStatus::Atom,
621 Action::Macro(_) => SequenceStatus::Atom,
622 Action::Tab(_) => SequenceStatus::Atom,
623 Action::Window(_) => SequenceStatus::Atom,
624 Action::KeywordLookup => SequenceStatus::Atom,
625 Action::NoOp => SequenceStatus::Atom,
626 Action::Prompt(_) => SequenceStatus::Atom,
627 Action::RedrawScreen => SequenceStatus::Atom,
628 Action::Scroll(_) => SequenceStatus::Atom,
629 Action::Search(_, _) => SequenceStatus::Atom,
630 Action::ShowInfoMessage(_) => SequenceStatus::Atom,
631 Action::Suspend => SequenceStatus::Atom,
632 }
633 }
634
635 pub fn is_last_selection(&self, ctx: &EditContext) -> SequenceStatus {
637 match self {
638 Action::Repeat(_) => SequenceStatus::Ignore,
639
640 Action::Application(act) => act.is_last_selection(ctx),
641 Action::Editor(act) => act.is_last_selection(ctx),
642
643 Action::Command(_) => SequenceStatus::Ignore,
644 Action::CommandBar(_) => SequenceStatus::Ignore,
645 Action::Jump(_, _, _) => SequenceStatus::Ignore,
646 Action::Macro(_) => SequenceStatus::Ignore,
647 Action::Tab(_) => SequenceStatus::Ignore,
648 Action::Window(_) => SequenceStatus::Ignore,
649 Action::KeywordLookup => SequenceStatus::Ignore,
650 Action::NoOp => SequenceStatus::Ignore,
651 Action::Prompt(_) => SequenceStatus::Ignore,
652 Action::RedrawScreen => SequenceStatus::Ignore,
653 Action::Scroll(_) => SequenceStatus::Ignore,
654 Action::Search(_, _) => SequenceStatus::Ignore,
655 Action::ShowInfoMessage(_) => SequenceStatus::Ignore,
656 Action::Suspend => SequenceStatus::Ignore,
657 }
658 }
659
660 pub fn is_switchable(&self, ctx: &EditContext) -> bool {
662 match self {
663 Action::Application(act) => act.is_switchable(ctx),
664 Action::Editor(act) => act.is_switchable(ctx),
665 Action::Jump(..) => true,
666
667 Action::CommandBar(_) => false,
668 Action::Command(_) => false,
669 Action::KeywordLookup => false,
670 Action::Macro(_) => false,
671 Action::NoOp => false,
672 Action::Prompt(_) => false,
673 Action::RedrawScreen => false,
674 Action::Repeat(_) => false,
675 Action::Scroll(_) => false,
676 Action::Search(_, _) => false,
677 Action::ShowInfoMessage(_) => false,
678 Action::Suspend => false,
679 Action::Tab(_) => false,
680 Action::Window(_) => false,
681 }
682 }
683}
684
685#[allow(clippy::derivable_impls)]
686impl<I: ApplicationInfo> Default for Action<I> {
687 fn default() -> Self {
688 Action::NoOp
689 }
690}
691
692impl<I: ApplicationInfo> From<SelectionAction> for Action<I> {
693 fn from(act: SelectionAction) -> Self {
694 Action::Editor(EditorAction::Selection(act))
695 }
696}
697
698impl<I: ApplicationInfo> From<InsertTextAction> for Action<I> {
699 fn from(act: InsertTextAction) -> Self {
700 Action::Editor(EditorAction::InsertText(act))
701 }
702}
703
704impl<I: ApplicationInfo> From<HistoryAction> for Action<I> {
705 fn from(act: HistoryAction) -> Self {
706 Action::Editor(EditorAction::History(act))
707 }
708}
709
710impl<I: ApplicationInfo> From<CursorAction> for Action<I> {
711 fn from(act: CursorAction) -> Self {
712 Action::Editor(EditorAction::Cursor(act))
713 }
714}
715
716impl<I: ApplicationInfo> From<EditorAction> for Action<I> {
717 fn from(act: EditorAction) -> Self {
718 Action::Editor(act)
719 }
720}
721
722impl<I: ApplicationInfo> From<MacroAction> for Action<I> {
723 fn from(act: MacroAction) -> Self {
724 Action::Macro(act)
725 }
726}
727
728impl<I: ApplicationInfo> From<CommandAction> for Action<I> {
729 fn from(act: CommandAction) -> Self {
730 Action::Command(act)
731 }
732}
733
734impl<I: ApplicationInfo> From<CommandBarAction<I>> for Action<I> {
735 fn from(act: CommandBarAction<I>) -> Self {
736 Action::CommandBar(act)
737 }
738}
739
740impl<I: ApplicationInfo> From<PromptAction> for Action<I> {
741 fn from(act: PromptAction) -> Self {
742 Action::Prompt(act)
743 }
744}
745
746impl<I: ApplicationInfo> From<WindowAction<I>> for Action<I> {
747 fn from(act: WindowAction<I>) -> Self {
748 Action::Window(act)
749 }
750}
751
752impl<I: ApplicationInfo> From<TabAction<I>> for Action<I> {
753 fn from(act: TabAction<I>) -> Self {
754 Action::Tab(act)
755 }
756}
757
758#[cfg(test)]
759mod tests {
760 use super::*;
761
762 #[test]
763 fn test_is_readonly() {
764 let mut ctx = EditContext::default();
765
766 let act = SelectionAction::Duplicate(MoveDir1D::Next, Count::Contextual);
767 assert_eq!(EditorAction::from(act).is_readonly(&ctx), true);
768
769 let act = HistoryAction::Checkpoint;
770 assert_eq!(EditorAction::from(act).is_readonly(&ctx), true);
771
772 let act = HistoryAction::Undo(Count::Contextual);
773 assert_eq!(EditorAction::from(act).is_readonly(&ctx), false);
774
775 let act = EditorAction::Edit(Specifier::Contextual, EditTarget::CurrentPosition);
776 ctx.operation = EditAction::Motion;
777 assert_eq!(act.is_readonly(&ctx), true);
778
779 let act = EditorAction::Edit(Specifier::Contextual, EditTarget::CurrentPosition);
780 ctx.operation = EditAction::Delete;
781 assert_eq!(act.is_readonly(&ctx), false);
782 }
783}