editor_types/lib.rs
1//! # Editor Types
2//!
3//! ## Overview
4//!
5//! The types in this crate provides a defunctionalized view of a text editor. Consumers of these
6//! types should map them into text manipulation or user interface actions.
7//!
8//! ## Examples
9//!
10//! ```
11//! use editor_types::{Action, EditAction, EditorAction};
12//! use editor_types::prelude::*;
13//!
14//! // Delete the current text selection.
15//! let _: Action = EditorAction::Edit(EditAction::Delete.into(), EditTarget::Selection).into();
16//!
17//! // Copy the next three lines.
18//! let _: Action = EditorAction::Edit(EditAction::Yank.into(), EditTarget::Range(RangeType::Line, true, 3.into())).into();
19//!
20//! // Make some contextually specified number of words lowercase.
21//! let _: Action = EditorAction::Edit(
22//! EditAction::ChangeCase(Case::Lower).into(),
23//! EditTarget::Motion(MoveType::WordBegin(WordStyle::Big, MoveDir1D::Next), Count::Contextual)
24//! ).into();
25//!
26//! // Scroll the viewport so that line 10 is at the top of the screen.
27//! let _: Action = Action::Scroll(ScrollStyle::LinePos(MovePosition::Beginning, 10.into()));
28//! ```
29pub 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/// A macro that turns a shorthand command DSL into an [Action].
40pub use editor_types_macros::action;
41
42/// The various actions that can be taken on text.
43#[derive(Clone, Debug, Default, Eq, PartialEq)]
44pub enum EditAction {
45 /// Move the cursor.
46 ///
47 /// If a shape is [specified contextually](EditContext::get_target_shape), then visually select
48 /// text while moving, as if using [SelectionAction::Resize] with
49 /// [SelectionResizeStyle::Extend].
50 #[default]
51 Motion,
52
53 /// Delete the targeted text.
54 Delete,
55
56 /// Yank the targeted text into a [Register].
57 Yank,
58
59 /// Replace characters within the targeted text with a new character.
60 ///
61 /// If [bool] is true, virtually replace characters by how many columns they occupy.
62 Replace(bool),
63
64 /// Automatically format the targeted text.
65 Format,
66
67 /// Change the first number on each line within the targeted text.
68 ///
69 /// The [bool] argument controls whether to increment by an additional count on each line.
70 ChangeNumber(NumberChange, bool),
71
72 /// Join the lines within the targeted text together.
73 Join(JoinStyle),
74
75 /// Change the indent level of the targeted text.
76 Indent(IndentChange),
77
78 /// Change the case of the targeted text.
79 ChangeCase(Case),
80}
81
82impl EditAction {
83 /// Returns true if this [EditAction] doesn't modify a buffer's text.
84 pub fn is_readonly(&self) -> bool {
85 match self {
86 EditAction::Motion => true,
87 EditAction::Yank => true,
88
89 EditAction::ChangeCase(_) => false,
90 EditAction::ChangeNumber(_, _) => false,
91 EditAction::Delete => false,
92 EditAction::Format => false,
93 EditAction::Indent(_) => false,
94 EditAction::Join(_) => false,
95 EditAction::Replace(_) => false,
96 }
97 }
98
99 /// Returns true if the value is [EditAction::Motion].
100 pub fn is_motion(&self) -> bool {
101 matches!(self, EditAction::Motion)
102 }
103
104 /// Returns true if this [EditAction] is allowed to trigger a [WindowAction::Switch] after an
105 /// error.
106 pub fn is_switchable(&self, _: &EditContext) -> bool {
107 self.is_motion()
108 }
109}
110
111/// Actions for manipulating text selections.
112#[derive(Clone, Debug, Eq, PartialEq)]
113#[non_exhaustive]
114pub enum SelectionAction {
115 /// Duplicate selections [*n* times](Count) to adjacent lines in [MoveDir1D] direction.
116 ///
117 /// If the column positions are too large to fit on the adjacent lines, then the next line
118 /// large enough to hold the selection is used instead.
119 ///
120 /// ## Example: Using `action!`
121 ///
122 /// ```
123 /// use editor_types::prelude::*;
124 /// use editor_types::{action, Action, SelectionAction};
125 ///
126 /// let count = Count::Contextual;
127 /// let act: Action = SelectionAction::Duplicate(MoveDir1D::Next, count.clone()).into();
128 ///
129 /// // All of these are equivalent:
130 /// assert_eq!(action!("selection duplicate -d next"), act);
131 /// assert_eq!(action!("selection duplicate -d next -c ctx"), act);
132 /// assert_eq!(action!("selection duplicate -d next -c {count}"), act);
133 /// ```
134 Duplicate(MoveDir1D, Count),
135
136 /// Change the placement of the cursor and anchor of a visual selection.
137 ///
138 /// ## Example: Using `action!`
139 ///
140 /// ```
141 /// use editor_types::prelude::*;
142 /// use editor_types::{action, Action, SelectionAction};
143 ///
144 /// let change = SelectionCursorChange::End;
145 /// let act: Action = action!("selection cursor-set -f end");
146 /// assert_eq!(act, SelectionAction::CursorSet(change).into());
147 /// ```
148 CursorSet(SelectionCursorChange),
149
150 /// Expand a selection by repositioning its cursor and anchor such that they are placed on the
151 /// specified boundary.
152 ///
153 /// Be aware that since this repositions the start and end of the selection, this may not do
154 /// what you want with [TargetShape::BlockWise] selections.
155 ///
156 /// ## Example: Using `action!`
157 ///
158 /// ```
159 /// use editor_types::prelude::*;
160 /// use editor_types::{action, Action, SelectionAction};
161 ///
162 /// let style = SelectionBoundary::Line;
163 /// let split: Action = action!("selection expand -b line -t all");
164 /// assert_eq!(split, SelectionAction::Expand(style, TargetShapeFilter::ALL).into());
165 /// ```
166 Expand(SelectionBoundary, TargetShapeFilter),
167
168 /// Filter selections using the last regular expression entered for [CommandType::Search].
169 ///
170 /// ## Example: Using `action!`
171 ///
172 /// ```
173 /// use editor_types::prelude::*;
174 /// use editor_types::{action, Action, SelectionAction};
175 ///
176 /// let act = SelectionAction::Filter(MatchAction::Keep);
177 /// let split: Action = action!("selection filter -F keep");
178 /// assert_eq!(split, act.into());
179 /// ```
180 Filter(MatchAction),
181
182 /// Join adjacent selections together.
183 ///
184 /// ## Example: Using `action!`
185 ///
186 /// ```
187 /// use editor_types::prelude::*;
188 /// use editor_types::{action, Action, SelectionAction};
189 ///
190 /// let act: Action = SelectionAction::Join.into();
191 /// assert_eq!(act, action!("selection join"));
192 /// ```
193 Join,
194
195 /// Change the bounds of the current selection as described by the
196 /// [style](SelectionResizeStyle) and [target](EditTarget).
197 ///
198 /// If the context doesn't specify a selection shape, then the selection will determine its
199 /// shape from the [EditTarget].
200 ///
201 /// See the documentation for the [SelectionResizeStyle] variants for how to construct all of the
202 /// possible values using [action].
203 Resize(SelectionResizeStyle, EditTarget),
204
205 /// Split [matching selections](TargetShapeFilter) into multiple selections line.
206 ///
207 /// All of the new selections are of the same shape as the one they were split from.
208 ///
209 /// ## Example: Using `action!`
210 ///
211 /// ```
212 /// use editor_types::prelude::*;
213 /// use editor_types::{action, Action, SelectionAction};
214 ///
215 /// let style = SelectionSplitStyle::Lines;
216 /// let split: Action = action!("selection split -s lines -F all");
217 /// assert_eq!(split, SelectionAction::Split(style, TargetShapeFilter::ALL).into());
218 /// ```
219 Split(SelectionSplitStyle, TargetShapeFilter),
220
221 /// Shrink a selection by repositioning its cursor and anchor such that they are placed on the
222 /// specified boundary.
223 ///
224 /// Be aware that since this repositions the start and end of the selection, this may not do
225 /// what you want with [TargetShape::BlockWise] selections.
226 ///
227 /// ## Example: Using `action!`
228 ///
229 /// ```
230 /// use editor_types::prelude::*;
231 /// use editor_types::{action, Action, SelectionAction};
232 ///
233 /// let style = SelectionBoundary::Line;
234 /// let split: Action = action!("selection trim -b line -t all");
235 /// assert_eq!(split, SelectionAction::Trim(style, TargetShapeFilter::ALL).into());
236 /// ```
237 Trim(SelectionBoundary, TargetShapeFilter),
238}
239
240/// Actions for inserting text into a buffer.
241#[derive(Clone, Debug, Eq, PartialEq)]
242#[non_exhaustive]
243pub enum InsertTextAction {
244 /// Insert a new line [shape-wise](TargetShape) before or after the current position.
245 ///
246 /// ## Example: Using `action!`
247 ///
248 /// ```
249 /// use editor_types::prelude::*;
250 /// use editor_types::{action, Action, InsertTextAction};
251 ///
252 /// let shape = TargetShape::LineWise;
253 /// let count = Count::Contextual;
254 /// let act: Action = InsertTextAction::OpenLine(shape, MoveDir1D::Next, count).into();
255 ///
256 /// // All of these are equivalent:
257 /// assert_eq!(act, action!("insert open-line -S line -d next -c ctx"));
258 /// assert_eq!(act, action!("insert open-line -S line -d next"));
259 /// ```
260 OpenLine(TargetShape, MoveDir1D, Count),
261
262 /// Paste before or after the current cursor position [*n*](Count) times.
263 ///
264 /// ## Example: Using `action!`
265 ///
266 /// ```
267 /// use editor_types::prelude::*;
268 /// use editor_types::{action, Action, InsertTextAction};
269 ///
270 /// let count = 5;
271 /// let paste: Action = action!("insert paste -s (side -d next) -c {count}");
272 /// assert_eq!(paste, InsertTextAction::Paste(PasteStyle::Side(MoveDir1D::Next), Count::Exact(5)).into());
273 /// ```
274 Paste(PasteStyle, Count),
275
276 /// Insert the contents of a [String] on [either side](MoveDir1D) of the cursor.
277 ///
278 /// ## Example: Using `action!`
279 ///
280 /// ```
281 /// use editor_types::prelude::*;
282 /// use editor_types::{action, Action, InsertTextAction};
283 ///
284 /// let input: Action = action!(r#"insert transcribe -i "hello" -d next -c 1"#);
285 /// assert_eq!(input, InsertTextAction::Transcribe("hello".into(), MoveDir1D::Next, 1.into()).into());
286 /// ```
287 Transcribe(String, MoveDir1D, Count),
288
289 /// Type a [character](Char) on [either side](MoveDir1D) of the cursor [*n*](Count) times.
290 ///
291 /// ## Example: Using `action!`
292 ///
293 /// ```
294 /// use editor_types::prelude::*;
295 /// use editor_types::{action, Action, InsertTextAction};
296 ///
297 /// let c = Specifier::Exact(Char::from('a'));
298 /// let dir = MoveDir1D::Previous;
299 /// let count = Count::Contextual;
300 /// let act: Action = InsertTextAction::Type(c.clone(), dir, count).into();
301 ///
302 /// // All of these are equivalent:
303 /// assert_eq!(act, action!("insert type -i (exact \'a\') -d previous -c ctx"));
304 /// assert_eq!(act, action!("insert type -i (exact \'a\') -c ctx"));
305 /// assert_eq!(act, action!("insert type -i (exact \'a\') -d previous"));
306 /// assert_eq!(act, action!("insert type -i (exact \'a\')"));
307 /// assert_eq!(act, action!("insert type -i {c}"));
308 /// ```
309 Type(Specifier<Char>, MoveDir1D, Count),
310}
311
312/// Actions for manipulating a buffer's history.
313#[derive(Clone, Debug, Eq, PartialEq)]
314pub enum HistoryAction {
315 /// Create a new editing history checkpoint.
316 ///
317 /// ## Example: Using `action!`
318 ///
319 /// ```
320 /// use editor_types::prelude::*;
321 /// use editor_types::{action, Action, HistoryAction};
322 ///
323 /// let check: Action = action!("history checkpoint");
324 /// assert_eq!(check, HistoryAction::Checkpoint.into());
325 /// ```
326 Checkpoint,
327
328 /// Redo [*n*](Count) edits.
329 ///
330 /// ## Example: Using `action!`
331 ///
332 /// ```
333 /// use editor_types::prelude::*;
334 /// use editor_types::{action, Action, HistoryAction};
335 ///
336 /// let redo: Action = action!("history redo");
337 /// assert_eq!(redo, HistoryAction::Redo(Count::Contextual).into());
338 ///
339 /// let redo: Action = action!("history redo -c 1");
340 /// assert_eq!(redo, HistoryAction::Redo(Count::Exact(1)).into());
341 /// ```
342 Redo(Count),
343
344 /// Undo [*n*](Count) edits.
345 ///
346 /// ```
347 /// use editor_types::prelude::*;
348 /// use editor_types::{action, Action, HistoryAction};
349 ///
350 /// let undo: Action = action!("history undo");
351 /// assert_eq!(undo, HistoryAction::Undo(Count::Contextual).into());
352 ///
353 /// let undo: Action = action!("history undo -c 1");
354 /// assert_eq!(undo, HistoryAction::Undo(Count::Exact(1)).into());
355 /// ```
356 Undo(Count),
357}
358
359impl HistoryAction {
360 /// Returns true if this [HistoryAction] doesn't modify a buffer's text.
361 pub fn is_readonly(&self) -> bool {
362 match self {
363 HistoryAction::Redo(_) => false,
364 HistoryAction::Undo(_) => false,
365 HistoryAction::Checkpoint => true,
366 }
367 }
368}
369
370/// Actions for manipulating cursor groups.
371#[derive(Clone, Debug, Eq, PartialEq)]
372#[non_exhaustive]
373pub enum CursorAction {
374 /// Close the [targeted cursors](CursorCloseTarget) in the current cursor group.
375 ///
376 /// ## Example: Using `action!`
377 ///
378 /// ```
379 /// use editor_types::prelude::*;
380 /// use editor_types::{action, Action, CursorAction};
381 ///
382 /// let close: Action = action!("cursor close -t leader");
383 /// assert_eq!(close, CursorAction::Close(CursorCloseTarget::Leader).into());
384 ///
385 /// let close: Action = action!("cursor close -t followers");
386 /// assert_eq!(close, CursorAction::Close(CursorCloseTarget::Followers).into());
387 /// ```
388 Close(CursorCloseTarget),
389
390 /// Restore a saved cursor group.
391 ///
392 /// If a combining style is specified, then the saved group will be merged with the current one
393 /// as specified.
394 ///
395 /// ## Example: Using `action!`
396 ///
397 /// ```
398 /// use editor_types::prelude::*;
399 /// use editor_types::{action, Action, CursorAction};
400 ///
401 /// let restore: Action = action!("cursor restore -s append");
402 /// assert_eq!(restore, CursorAction::Restore(CursorGroupCombineStyle::Append).into());
403 ///
404 /// let restore: Action = action!("cursor restore -s replace");
405 /// assert_eq!(restore, CursorAction::Restore(CursorGroupCombineStyle::Replace).into());
406 ///
407 /// let restore: Action = action!("cursor restore -s (merge select-cursor -d prev)");
408 /// assert_eq!(restore, CursorAction::Restore(CursorGroupCombineStyle::Merge(CursorMergeStyle::SelectCursor(MoveDir1D::Previous))).into());
409 /// ```
410 ///
411 /// See the documentation for [CursorGroupCombineStyle] for how to construct each of its
412 /// variants with [action].
413 Restore(CursorGroupCombineStyle),
414
415 /// Rotate which cursor in the cursor group is the current leader .
416 ///
417 /// ## Example: Using `action!`
418 ///
419 /// ```
420 /// use editor_types::prelude::*;
421 /// use editor_types::{action, Action, CursorAction};
422 ///
423 /// let rotate: Action = action!("cursor rotate -d prev");
424 /// assert_eq!(rotate, CursorAction::Rotate(MoveDir1D::Previous, Count::Contextual).into());
425 ///
426 /// let rotate: Action = action!("cursor rotate -d next -c 2");
427 /// assert_eq!(rotate, CursorAction::Rotate(MoveDir1D::Next, Count::Exact(2)).into());
428 /// ```
429 Rotate(MoveDir1D, Count),
430
431 /// Save the current cursor group.
432 ///
433 /// If a combining style is specified, then the current group will be merged with any
434 /// previously saved group as specified.
435 ///
436 /// ## Example: Using `action!`
437 ///
438 /// ```
439 /// use editor_types::prelude::*;
440 /// use editor_types::{action, Action, CursorAction};
441 ///
442 /// let save: Action = action!("cursor save -s append");
443 /// assert_eq!(save, CursorAction::Save(CursorGroupCombineStyle::Append).into());
444 ///
445 /// let save: Action = action!("cursor save -s replace");
446 /// assert_eq!(save, CursorAction::Save(CursorGroupCombineStyle::Replace).into());
447 ///
448 /// let save: Action = action!("cursor save -s (merge union)");
449 /// assert_eq!(save, CursorAction::Save(CursorGroupCombineStyle::Merge(CursorMergeStyle::Union)).into());
450 /// ```
451 ///
452 /// See the documentation for [CursorGroupCombineStyle] for how to construct each of its
453 /// variants with [action].
454 Save(CursorGroupCombineStyle),
455
456 /// Split each cursor in the cursor group [*n*](Count) times.
457 ///
458 /// ## Example: Using `action!`
459 ///
460 /// ```
461 /// use editor_types::prelude::*;
462 /// use editor_types::{action, Action, CursorAction};
463 ///
464 /// let split: Action = action!("cursor split -c {}", Count::Contextual);
465 /// assert_eq!(split, CursorAction::Split(Count::Contextual).into());
466 ///
467 /// let split: Action = action!("cursor split -c {}", 5);
468 /// assert_eq!(split, CursorAction::Split(Count::Exact(5)).into());
469 /// ```
470 Split(Count),
471}
472
473impl CursorAction {
474 /// Returns true if this [CursorAction] is allowed to trigger a [WindowAction::Switch] after an
475 /// error.
476 pub fn is_switchable(&self, _: &EditContext) -> bool {
477 match self {
478 CursorAction::Restore(_) => true,
479
480 CursorAction::Close(_) => false,
481 CursorAction::Rotate(..) => false,
482 CursorAction::Save(_) => false,
483 CursorAction::Split(_) => false,
484 }
485 }
486}
487
488/// Actions for running application commands (e.g. `:w` or `:quit`).
489#[derive(Clone, Debug, Eq, PartialEq)]
490#[non_exhaustive]
491pub enum CommandAction {
492 /// Run a command string.
493 ///
494 /// This should update [Register::LastCommand].
495 ///
496 /// ## Example: Using `action!`
497 ///
498 /// ```
499 /// use editor_types::prelude::*;
500 /// use editor_types::{action, Action, CommandAction};
501 ///
502 /// let quitall: Action = action!(r#"command run -i "quitall" "#);
503 /// assert_eq!(quitall, CommandAction::Run("quitall".into()).into());
504 /// ```
505 Run(String),
506
507 /// Execute the last [CommandType::Command] entry [*n* times](Count).
508 ///
509 /// ## Example: Using `action!`
510 ///
511 /// ```
512 /// use editor_types::prelude::*;
513 /// use editor_types::{action, Action, CommandAction};
514 ///
515 /// let exec: Action = action!("command execute");
516 /// assert_eq!(exec, CommandAction::Execute(Count::Contextual).into());
517 ///
518 /// let exec5: Action = action!("command execute -c 5");
519 /// assert_eq!(exec5, CommandAction::Execute(5.into()).into());
520 /// ```
521 Execute(Count),
522}
523
524/// Actions for manipulating the application's command bar.
525#[derive(Clone, Debug, Eq, PartialEq)]
526pub enum CommandBarAction<I: ApplicationInfo> {
527 /// Focus the command bar
528 ///
529 /// ## Example: Using `action!`
530 ///
531 /// ```
532 /// use editor_types::prelude::*;
533 /// use editor_types::{action, Action, CommandBarAction};
534 ///
535 /// let focus: Action = action!(r#"cmdbar focus -p "/" -s search -a (search -d same)"#);
536 /// assert_eq!(focus, CommandBarAction::Focus(
537 /// "/".into(),
538 /// CommandType::Search,
539 /// Action::Search(MoveDirMod::Same, Count::Contextual).into(),
540 /// ).into());
541 /// ```
542 Focus(String, CommandType, Box<Action<I>>),
543
544 /// Unfocus the command bar.
545 ///
546 /// ## Example: Using `action!`
547 ///
548 /// ```
549 /// use editor_types::prelude::*;
550 /// use editor_types::{action, Action, CommandBarAction};
551 ///
552 /// let unfocus: Action = action!("cmdbar unfocus");
553 /// assert_eq!(unfocus, CommandBarAction::Unfocus.into());
554 /// ```
555 Unfocus,
556}
557
558/// Actions for manipulating prompts.
559#[derive(Clone, Debug, Eq, PartialEq)]
560pub enum PromptAction {
561 /// Abort command entry.
562 ///
563 /// [bool] indicates whether this requires the prompt to be empty. (For example, how `<C-D>`
564 /// behaves in shells.)
565 ///
566 /// ## Example: Using `action!`
567 ///
568 /// ```
569 /// use editor_types::prelude::*;
570 /// use editor_types::{action, Action, PromptAction};
571 ///
572 /// let act: Action = action!("prompt abort");
573 /// let exp: Action = PromptAction::Abort(false).into();
574 /// assert_eq!(act, exp);
575 /// ```
576 Abort(bool),
577
578 /// Submit the currently entered text.
579 ///
580 /// ## Example: Using `action!`
581 ///
582 /// ```
583 /// use editor_types::prelude::*;
584 /// use editor_types::{action, Action, PromptAction};
585 ///
586 /// let act: Action = action!("prompt submit");
587 /// let exp: Action = PromptAction::Submit.into();
588 /// assert_eq!(act, exp);
589 /// ```
590 Submit,
591
592 /// Move backwards and forwards through previous entries.
593 ///
594 /// ## Example: Using `action!`
595 ///
596 /// ```
597 /// use editor_types::prelude::*;
598 /// use editor_types::{action, Action, PromptAction};
599 ///
600 /// let filter = RecallFilter::All;
601 /// let act: Action = PromptAction::Recall(filter.clone(), MoveDir1D::Next, Count::Contextual).into();
602 ///
603 /// // All of these are equivalent:
604 /// assert_eq!(act, action!("prompt recall -d next -c ctx -F all"));
605 /// assert_eq!(act, action!("prompt recall -d next -c ctx -F {filter}"));
606 /// assert_eq!(act, action!("prompt recall -d next -c ctx"));
607 /// assert_eq!(act, action!("prompt recall -d next"));
608 /// ```
609 Recall(RecallFilter, MoveDir1D, Count),
610}
611
612/// Actions for recording and running macros.
613#[derive(Clone, Debug, Eq, PartialEq)]
614#[non_exhaustive]
615pub enum MacroAction {
616 /// Execute the contents of the contextually specified Register [*n* times](Count).
617 ///
618 /// If no register is specified, then this should default to [Register::UnnamedMacro].
619 ///
620 /// ## Example: Using `action!`
621 ///
622 /// ```
623 /// use editor_types::prelude::*;
624 /// use editor_types::{action, Action, MacroAction};
625 ///
626 /// let act: Action = MacroAction::Execute(Count::Contextual).into();
627 ///
628 /// // All of these are equivalent:
629 /// assert_eq!(act, action!("macro execute -c ctx"));
630 /// assert_eq!(act, action!("macro execute"));
631 /// assert_eq!(act, action!("macro exec"));
632 /// ```
633 Execute(Count),
634
635 /// Run the given macro string [*n* times](Count).
636 ///
637 /// ## Example: Using `action!`
638 ///
639 /// ```
640 /// use editor_types::prelude::*;
641 /// use editor_types::{action, Action, MacroAction};
642 ///
643 /// let mac = "hjkl".to_string();
644 /// let act: Action = MacroAction::Run(mac, Count::Contextual).into();
645 ///
646 /// // All of these are equivalent:
647 /// assert_eq!(act, action!("macro run -i \"hjkl\" -c ctx"));
648 /// assert_eq!(act, action!("macro run -i \"hjkl\""));
649 /// ```
650 Run(String, Count),
651
652 /// Execute the contents of the previously specified register [*n* times](Count).
653 ///
654 /// ## Example: Using `action!`
655 ///
656 /// ```
657 /// use editor_types::prelude::*;
658 /// use editor_types::{action, Action, MacroAction};
659 ///
660 /// let act: Action = MacroAction::Repeat(Count::Contextual).into();
661 ///
662 /// // All of these are equivalent:
663 /// assert_eq!(act, action!("macro repeat -c ctx"));
664 /// assert_eq!(act, action!("macro repeat"));
665 /// ```
666 Repeat(Count),
667
668 /// Start or stop recording a macro.
669 ///
670 /// ## Example: Using `action!`
671 ///
672 /// ```
673 /// use editor_types::prelude::*;
674 /// use editor_types::{action, Action, MacroAction};
675 ///
676 /// let act: Action = MacroAction::ToggleRecording.into();
677 /// assert_eq!(act, action!("macro toggle-recording"));
678 /// ```
679 ToggleRecording,
680}
681
682/// Actions for manipulating application tabs.
683#[derive(Clone, Debug, Eq, PartialEq)]
684#[non_exhaustive]
685pub enum TabAction<I: ApplicationInfo> {
686 /// Close the [TabTarget] tabs with [CloseFlags] options.
687 ///
688 /// ## Example: Using `action!`
689 ///
690 /// ```
691 /// use editor_types::prelude::*;
692 /// use editor_types::{action, Action, TabAction};
693 ///
694 /// let fc = TabTarget::Single(FocusChange::Current);
695 /// let flags = CloseFlags::NONE;
696 /// let extract: Action = TabAction::Close(fc, flags).into();
697 /// assert_eq!(extract, action!("tab close -t (single current) -F none"));
698 /// ```
699 Close(TabTarget, CloseFlags),
700
701 /// Extract the currently focused window from the currently focused tab, and place it in a new
702 /// tab.
703 ///
704 /// If there is only one window in the current tab, then this does nothing.
705 ///
706 /// The new tab will be placed on [MoveDir1D] side of the tab targeted by [FocusChange]. If
707 /// [FocusChange] doesn't resolve to a valid tab, then the new tab is placed after the
708 /// currently focused tab.
709 ///
710 /// ## Example: Using `action!`
711 ///
712 /// ```
713 /// use editor_types::prelude::*;
714 /// use editor_types::{action, Action, TabAction};
715 ///
716 /// let extract: Action = TabAction::Extract(FocusChange::Current, MoveDir1D::Next).into();
717 /// assert_eq!(extract, action!("tab extract -f current -d next"));
718 /// ```
719 ///
720 /// See the documentation for [FocusChange] for how to construct each of its variants with
721 /// [action].
722 Extract(FocusChange, MoveDir1D),
723
724 /// Change the current focus to the tab targeted by [FocusChange].
725 ///
726 /// ## Example: Using `action!`
727 ///
728 /// ```
729 /// use editor_types::prelude::*;
730 /// use editor_types::{action, Action, TabAction};
731 ///
732 /// let extract: Action = TabAction::Focus(FocusChange::PreviouslyFocused).into();
733 /// assert_eq!(extract, action!("tab focus -f previously-focused"));
734 /// ```
735 ///
736 /// See the documentation for [FocusChange] for how to construct each of its variants with
737 /// [action].
738 Focus(FocusChange),
739
740 /// Move the currently focused tab to the position targeted by [FocusChange].
741 ///
742 /// ## Example: Using `action!`
743 ///
744 /// ```
745 /// use editor_types::prelude::*;
746 /// use editor_types::{action, Action, TabAction};
747 ///
748 /// let extract: Action = TabAction::Move(FocusChange::PreviouslyFocused).into();
749 /// assert_eq!(extract, action!("tab move -f previously-focused"));
750 /// ```
751 ///
752 /// See the documentation for [FocusChange] for how to construct each of its variants with
753 /// [action].
754 Move(FocusChange),
755
756 /// Open a new tab after the tab targeted by [FocusChange] that displays the requested content.
757 ///
758 /// ## Example: Using `action!`
759 ///
760 /// ```
761 /// use editor_types::prelude::*;
762 /// use editor_types::{action, Action, TabAction};
763 ///
764 /// let extract: Action = TabAction::Open(OpenTarget::Current, FocusChange::PreviouslyFocused).into();
765 /// assert_eq!(extract, action!("tab open -t current -f previously-focused"));
766 /// ```
767 ///
768 /// See the documentation for [OpenTarget] and [FocusChange] for how to construct each of their
769 /// variants with [action].
770 Open(OpenTarget<I::WindowId>, FocusChange),
771}
772
773/// Actions for manipulating application windows.
774#[derive(Clone, Debug, Eq, PartialEq)]
775#[non_exhaustive]
776pub enum WindowAction<I: ApplicationInfo> {
777 /// Close the [WindowTarget] windows with [CloseFlags] options.
778 ///
779 /// ## Example: Using `action!`
780 ///
781 /// ```
782 /// use editor_types::prelude::*;
783 /// use editor_types::{action, Action, WindowAction};
784 ///
785 /// let fc = WindowTarget::Single(FocusChange::Current);
786 /// let flags = CloseFlags::NONE;
787 /// let extract: Action = WindowAction::Close(fc, flags).into();
788 /// assert_eq!(extract, action!("window close -t (single current) -F none"));
789 /// ```
790 Close(WindowTarget, CloseFlags),
791
792 /// Exchange the currently focused window with the window targeted by [FocusChange].
793 ///
794 /// ## Example: Using `action!`
795 ///
796 /// ```
797 /// use editor_types::prelude::*;
798 /// use editor_types::{action, Action, WindowAction};
799 ///
800 /// let fc = FocusChange::PreviouslyFocused;
801 /// let act: Action = WindowAction::Exchange(fc).into();
802 /// assert_eq!(act, action!("window exchange -f previously-focused"));
803 /// ```
804 Exchange(FocusChange),
805
806 /// Change the current focus to the window targeted by [FocusChange].
807 ///
808 /// ## Example: Using `action!`
809 ///
810 /// ```
811 /// use editor_types::prelude::*;
812 /// use editor_types::{action, Action, WindowAction};
813 ///
814 /// let fc = FocusChange::PreviouslyFocused;
815 /// let act: Action = WindowAction::Focus(fc).into();
816 /// assert_eq!(act, action!("window focus -f previously-focused"));
817 /// ```
818 Focus(FocusChange),
819
820 /// Move the currently focused window to the [MoveDir2D] side of the screen.
821 ///
822 /// ## Example: Using `action!`
823 ///
824 /// ```
825 /// use editor_types::prelude::*;
826 /// use editor_types::{action, Action, WindowAction};
827 ///
828 /// let act: Action = WindowAction::MoveSide(MoveDir2D::Left).into();
829 /// assert_eq!(act, action!("window move-side -d left"));
830 /// ```
831 MoveSide(MoveDir2D),
832
833 /// Open a new window that is [*n*](Count) columns along [an axis](Axis), positioned relative to
834 /// the current window as indicated by [MoveDir1D].
835 ///
836 /// ## Example: Using `action!`
837 ///
838 /// ```
839 /// use editor_types::prelude::*;
840 /// use editor_types::{action, Action, WindowAction};
841 ///
842 /// let target = OpenTarget::Unnamed;
843 /// let axis = Axis::Horizontal;
844 /// let dir = MoveDir1D::Next;
845 /// let count = Count::Contextual;
846 /// let act: Action = WindowAction::Open(target, axis, dir, count).into();
847 ///
848 /// // All of these are equivalent:
849 /// assert_eq!(act, action!("window open -t unnamed -x horizontal -d next -c ctx"));
850 /// assert_eq!(act, action!("window open -t unnamed -x horizontal -d next"));
851 /// ```
852 Open(OpenTarget<I::WindowId>, Axis, MoveDir1D, Count),
853
854 /// Visually rotate the windows in [MoveDir2D] direction.
855 ///
856 /// ## Example: Using `action!`
857 ///
858 /// ```
859 /// use editor_types::prelude::*;
860 /// use editor_types::{action, Action, WindowAction};
861 ///
862 /// let act: Action = WindowAction::Rotate(MoveDir1D::Next).into();
863 /// assert_eq!(act, action!("window rotate -d next"));
864 /// ```
865 Rotate(MoveDir1D),
866
867 /// Split the currently focused window [*n* times](Count) along [an axis](Axis), moving
868 /// the focus in [MoveDir1D] direction after performing the split.
869 ///
870 /// ## Example: Using `action!`
871 ///
872 /// ```
873 /// use editor_types::prelude::*;
874 /// use editor_types::{action, Action, WindowAction};
875 ///
876 /// let target = OpenTarget::Current;
877 /// let axis = Axis::Vertical;
878 /// let dir = MoveDir1D::Next;
879 /// let count = Count::Contextual;
880 /// let act: Action = WindowAction::Split(target, axis, dir, count).into();
881 ///
882 /// // All of these are equivalent:
883 /// assert_eq!(act, action!("window split -t current -x vertical -d next -c ctx"));
884 /// assert_eq!(act, action!("window split -t current -x vertical -d next"));
885 /// ```
886 Split(OpenTarget<I::WindowId>, Axis, MoveDir1D, Count),
887
888 /// Switch what content the window is currently showing.
889 ///
890 /// If there are no currently open windows in the tab, then this behaves like
891 /// [WindowAction::Open].
892 ///
893 /// ## Example: Using `action!`
894 ///
895 /// ```
896 /// use editor_types::prelude::*;
897 /// use editor_types::{action, Action, WindowAction};
898 ///
899 /// let target = OpenTarget::Offset(MoveDir1D::Next, 5.into());
900 /// let switch: Action = WindowAction::Switch(target).into();
901 /// assert_eq!(switch, action!("window switch -t (offset -d next -c 5)"));
902 /// ```
903 Switch(OpenTarget<I::WindowId>),
904
905 /// Clear all of the explicitly set window sizes, and instead try to equally distribute
906 /// available rows and columns.
907 ///
908 /// ## Example: Using `action!`
909 ///
910 /// ```
911 /// use editor_types::prelude::*;
912 /// use editor_types::{action, Action, WindowAction};
913 ///
914 /// let act: Action = WindowAction::ClearSizes.into();
915 /// assert_eq!(act, action!("window clear-sizes"));
916 /// ```
917 ClearSizes,
918
919 /// Resize the window targeted by [FocusChange] according to [SizeChange].
920 ///
921 /// ## Example: Using `action!`
922 ///
923 /// ```
924 /// use editor_types::prelude::*;
925 /// use editor_types::{action, Action, WindowAction};
926 ///
927 /// let size = SizeChange::Equal;
928 /// let act: Action = WindowAction::Resize(FocusChange::Current, Axis::Vertical, size).into();
929 /// assert_eq!(act, action!("window resize -f current -x vertical -z equal"));
930 /// ```
931 Resize(FocusChange, Axis, SizeChange),
932
933 /// Write the contents of the windows targeted by [WindowTarget].
934 ///
935 /// ## Example: Using `action!`
936 ///
937 /// ```
938 /// use editor_types::prelude::*;
939 /// use editor_types::{action, Action, WindowAction};
940 ///
941 /// let target = WindowTarget::All;
942 /// let flags = WriteFlags::NONE;
943 /// let act: Action = WindowAction::Write(target, None, flags).into();
944 /// assert_eq!(act, action!("window write -t all -F none"));
945 /// ```
946 Write(WindowTarget, Option<String>, WriteFlags),
947
948 /// Zoom in on the currently focused window so that it takes up the whole screen. If there is
949 /// already a zoomed-in window, then return to showing all windows.
950 ///
951 /// ## Example: Using `action!`
952 ///
953 /// ```
954 /// use editor_types::prelude::*;
955 /// use editor_types::{action, Action, WindowAction};
956 ///
957 /// let act: Action = WindowAction::ZoomToggle.into();
958 /// assert_eq!(act, action!("window zoom-toggle"));
959 /// ```
960 ZoomToggle,
961}
962
963/// Actions for editing text within buffer.
964#[derive(Clone, Debug, Eq, PartialEq)]
965#[non_exhaustive]
966pub enum EditorAction {
967 /// Complete the text before the cursor group leader.
968 ///
969 /// See the documentation for the [CompletionStyle] variants for how to construct all of the
970 /// different [EditorAction::Complete] values using [action].
971 ///
972 /// ## Example: Using `action!`
973 ///
974 /// ```
975 /// use editor_types::prelude::*;
976 /// use editor_types::{action, Action, EditorAction};
977 ///
978 /// let ct = CompletionType::Auto;
979 /// let style = CompletionStyle::Prefix;
980 /// let display = CompletionDisplay::List;
981 /// let act: Action = EditorAction::Complete(style, ct.clone(), display).into();
982 ///
983 /// // All of these are equivalent:
984 /// assert_eq!(act, action!("complete -s prefix -T auto -D list"));
985 /// assert_eq!(act, action!("complete -s prefix -T {ct} -D list"));
986 /// assert_eq!(act, action!("complete -s prefix -D list"));
987 /// ```
988 Complete(CompletionStyle, CompletionType, CompletionDisplay),
989
990 /// Modify the current cursor group.
991 ///
992 /// See the documentation for the [CursorAction] variants for how to construct all of the
993 /// different [EditorAction::Cursor] values using [action].
994 ///
995 /// ## Example: Using `action!`
996 ///
997 /// ```
998 /// use editor_types::prelude::*;
999 /// use editor_types::{action, Action, CursorAction};
1000 ///
1001 /// let close: Action = action!("cursor close -t leader");
1002 /// assert_eq!(close, CursorAction::Close(CursorCloseTarget::Leader).into());
1003 ///
1004 /// let restore: Action = action!("cursor restore -s append");
1005 /// assert_eq!(restore, CursorAction::Restore(CursorGroupCombineStyle::Append).into());
1006 /// ```
1007 Cursor(CursorAction),
1008
1009 /// Perform the specified [action](EditAction) on [a target](EditTarget).
1010 ///
1011 /// ## Example: Using `action!`
1012 ///
1013 /// ```
1014 /// use editor_types::prelude::*;
1015 /// use editor_types::{action, Action, EditorAction};
1016 ///
1017 /// let ctx = Specifier::Contextual;
1018 /// let target = EditTarget::CurrentPosition;
1019 /// let act: Action = EditorAction::Edit(ctx, target.clone()).into();
1020 /// assert_eq!(act, action!("edit -o ctx -t {target}"));
1021 /// ```
1022 Edit(Specifier<EditAction>, EditTarget),
1023
1024 /// Perform a history operation.
1025 ///
1026 /// See the documentation for the [HistoryAction] variants for how to construct all of the
1027 /// different [EditorAction::History] values using [action].
1028 ///
1029 /// ```
1030 /// use editor_types::prelude::*;
1031 /// use editor_types::{action, Action, HistoryAction};
1032 ///
1033 /// let undo: Action = action!("history undo");
1034 /// assert_eq!(undo, HistoryAction::Undo(Count::Contextual).into());
1035 ///
1036 /// let redo: Action = action!("history redo");
1037 /// assert_eq!(redo, HistoryAction::Redo(Count::Contextual).into());
1038 /// ```
1039 History(HistoryAction),
1040
1041 /// Insert text.
1042 ///
1043 /// See the documentation for the [InsertTextAction] variants for how to construct all of the
1044 /// different [EditorAction::InsertText] values using [action].
1045 ///
1046 /// ## Example: Using `action!`
1047 ///
1048 /// ```
1049 /// use editor_types::prelude::*;
1050 /// use editor_types::{action, Action, InsertTextAction};
1051 ///
1052 /// let paste: Action = action!("insert paste -s cursor -c 10");
1053 /// assert_eq!(paste, InsertTextAction::Paste(PasteStyle::Cursor, 10.into()).into());
1054 /// ```
1055 InsertText(InsertTextAction),
1056
1057 /// Create a new [Mark] at the current leader position.
1058 ///
1059 /// ## Example: Using `action!`
1060 ///
1061 /// ```
1062 /// use editor_types::prelude::*;
1063 /// use editor_types::{action, Action, EditorAction};
1064 ///
1065 /// let mark = Mark::LastYankedBegin;
1066 /// let set_mark: Action = action!("mark -m {}", mark.clone());
1067 /// assert_eq!(set_mark, EditorAction::Mark(mark.into()).into());
1068 ///
1069 /// let set_mark: Action = action!("mark -m ctx");
1070 /// assert_eq!(set_mark, EditorAction::Mark(Specifier::Contextual).into());
1071 /// ```
1072 Mark(Specifier<Mark>),
1073
1074 /// Modify the current selection.
1075 ///
1076 /// See the documentation for the [SelectionAction] variants for how to construct all of the
1077 /// different [EditorAction::Selection] values using [action].
1078 ///
1079 /// ## Example: Using `action!`
1080 ///
1081 /// ```
1082 /// use editor_types::prelude::*;
1083 /// use editor_types::{action, Action, SelectionAction};
1084 ///
1085 /// let act: Action = SelectionAction::Duplicate(MoveDir1D::Next, Count::Contextual).into();
1086 /// assert_eq!(act, action!("selection duplicate -d next"));
1087 /// ```
1088 Selection(SelectionAction),
1089}
1090
1091impl EditorAction {
1092 /// Indicates if this is a read-only action.
1093 pub fn is_readonly(&self, ctx: &EditContext) -> bool {
1094 match self {
1095 EditorAction::Complete(_, _, _) => false,
1096 EditorAction::History(act) => act.is_readonly(),
1097 EditorAction::InsertText(_) => false,
1098
1099 EditorAction::Cursor(_) => true,
1100 EditorAction::Mark(_) => true,
1101 EditorAction::Selection(_) => true,
1102
1103 EditorAction::Edit(act, _) => ctx.resolve(act).is_readonly(),
1104 }
1105 }
1106
1107 /// Indicates how an action gets included in [RepeatType::EditSequence].
1108 ///
1109 /// `motion` indicates what to do with [EditAction::Motion].
1110 pub fn is_edit_sequence(&self, motion: SequenceStatus, ctx: &EditContext) -> SequenceStatus {
1111 match self {
1112 EditorAction::History(_) => SequenceStatus::Break,
1113 EditorAction::Mark(_) => SequenceStatus::Break,
1114 EditorAction::InsertText(_) => SequenceStatus::Track,
1115 EditorAction::Cursor(_) => SequenceStatus::Track,
1116 EditorAction::Selection(_) => SequenceStatus::Track,
1117 EditorAction::Complete(_, _, _) => SequenceStatus::Track,
1118 EditorAction::Edit(act, _) => {
1119 match ctx.resolve(act) {
1120 EditAction::Motion => motion,
1121 EditAction::Yank => SequenceStatus::Ignore,
1122 _ => SequenceStatus::Track,
1123 }
1124 },
1125 }
1126 }
1127
1128 /// Indicates how an action gets included in [RepeatType::LastAction].
1129 pub fn is_last_action(&self, _: &EditContext) -> SequenceStatus {
1130 match self {
1131 EditorAction::History(HistoryAction::Checkpoint) => SequenceStatus::Ignore,
1132 EditorAction::History(HistoryAction::Undo(_)) => SequenceStatus::Atom,
1133 EditorAction::History(HistoryAction::Redo(_)) => SequenceStatus::Atom,
1134
1135 EditorAction::Complete(_, _, _) => SequenceStatus::Atom,
1136 EditorAction::Cursor(_) => SequenceStatus::Atom,
1137 EditorAction::Edit(_, _) => SequenceStatus::Atom,
1138 EditorAction::InsertText(_) => SequenceStatus::Atom,
1139 EditorAction::Mark(_) => SequenceStatus::Atom,
1140 EditorAction::Selection(_) => SequenceStatus::Atom,
1141 }
1142 }
1143
1144 /// Indicates how an action gets included in [RepeatType::LastSelection].
1145 pub fn is_last_selection(&self, ctx: &EditContext) -> SequenceStatus {
1146 match self {
1147 EditorAction::History(_) => SequenceStatus::Ignore,
1148 EditorAction::Mark(_) => SequenceStatus::Ignore,
1149 EditorAction::InsertText(_) => SequenceStatus::Ignore,
1150 EditorAction::Cursor(_) => SequenceStatus::Ignore,
1151 EditorAction::Complete(_, _, _) => SequenceStatus::Ignore,
1152
1153 EditorAction::Selection(SelectionAction::Resize(_, _)) => SequenceStatus::Track,
1154 EditorAction::Selection(_) => SequenceStatus::Ignore,
1155
1156 EditorAction::Edit(act, _) => {
1157 if let EditAction::Motion = ctx.resolve(act) {
1158 if ctx.get_target_shape().is_some() {
1159 SequenceStatus::Restart
1160 } else {
1161 SequenceStatus::Ignore
1162 }
1163 } else {
1164 SequenceStatus::Ignore
1165 }
1166 },
1167 }
1168 }
1169
1170 /// Returns true if this [Action] is allowed to trigger a [WindowAction::Switch] after an error.
1171 pub fn is_switchable(&self, ctx: &EditContext) -> bool {
1172 match self {
1173 EditorAction::Cursor(act) => act.is_switchable(ctx),
1174 EditorAction::Edit(act, _) => ctx.resolve(act).is_switchable(ctx),
1175 EditorAction::Complete(_, _, _) => false,
1176 EditorAction::History(_) => false,
1177 EditorAction::InsertText(_) => false,
1178 EditorAction::Mark(_) => false,
1179 EditorAction::Selection(_) => false,
1180 }
1181 }
1182}
1183
1184impl From<CursorAction> for EditorAction {
1185 fn from(act: CursorAction) -> Self {
1186 EditorAction::Cursor(act)
1187 }
1188}
1189
1190impl From<HistoryAction> for EditorAction {
1191 fn from(act: HistoryAction) -> Self {
1192 EditorAction::History(act)
1193 }
1194}
1195
1196impl From<InsertTextAction> for EditorAction {
1197 fn from(act: InsertTextAction) -> Self {
1198 EditorAction::InsertText(act)
1199 }
1200}
1201
1202impl From<SelectionAction> for EditorAction {
1203 fn from(act: SelectionAction) -> Self {
1204 EditorAction::Selection(act)
1205 }
1206}
1207
1208/// The result of either pressing a complete keybinding sequence, or parsing a command.
1209#[derive(Clone, Debug, Eq, PartialEq)]
1210#[non_exhaustive]
1211pub enum Action<I: ApplicationInfo = EmptyInfo> {
1212 /// Do nothing.
1213 ///
1214 /// ## Example: Using `action!`
1215 ///
1216 /// ```
1217 /// use editor_types::{action, Action};
1218 ///
1219 /// // All of these are equivalent:
1220 /// let noop: Action = Action::NoOp;
1221 /// assert_eq!(action!("nop"), noop);
1222 /// assert_eq!(action!("noop"), noop);
1223 /// assert_eq!(action!("no-op"), noop);
1224 /// assert_eq!(Action::default(), noop);
1225 /// ```
1226 NoOp,
1227
1228 /// Perform an editor action.
1229 ///
1230 /// See the documentation for the [EditorAction] variants for how to construct all of the
1231 /// different [Action::Editor] values using [action].
1232 Editor(EditorAction),
1233
1234 /// Perform a macro-related action.
1235 ///
1236 /// See the documentation for the [MacroAction] variants for how to construct all of the
1237 /// different [Action::Macro] values using [action].
1238 Macro(MacroAction),
1239
1240 /// Navigate through the cursor positions in [the specified list](PositionList).
1241 ///
1242 /// If the current window cannot satisfy the given [Count], then this may jump to other
1243 /// windows.
1244 ///
1245 /// ## Example: Using `action!`
1246 ///
1247 /// ```
1248 /// use editor_types::prelude::*;
1249 /// use editor_types::{action, Action, InsertTextAction};
1250 ///
1251 /// let list = PositionList::JumpList;
1252 /// let count = Count::Contextual;
1253 ///
1254 /// let act: Action = Action::Jump(list, MoveDir1D::Next, count.clone());
1255 /// assert_eq!(act, action!("jump -t jump-list -d next -c ctx"));
1256 ///
1257 /// let act: Action = Action::Jump(list, MoveDir1D::Previous, count);
1258 /// assert_eq!(act, action!("jump -t jump-list -d previous -c ctx"));
1259 /// ```
1260 Jump(PositionList, MoveDir1D, Count),
1261
1262 /// Repeat an action sequence with the current context.
1263 ///
1264 /// ## Example: Using `action!`
1265 ///
1266 /// ```
1267 /// use editor_types::prelude::*;
1268 /// use editor_types::{action, Action};
1269 ///
1270 /// let rep: Action = action!("repeat -s edit-sequence");
1271 /// assert_eq!(rep, Action::Repeat(RepeatType::EditSequence));
1272 /// ```
1273 ///
1274 /// See the [RepeatType] documentation for how to construct each of its variants.
1275 Repeat(RepeatType),
1276
1277 /// Scroll the viewport in [the specified manner](ScrollStyle).
1278 ///
1279 /// ## Example: Using `action!`
1280 ///
1281 /// ```
1282 /// use editor_types::prelude::*;
1283 /// use editor_types::{action, Action};
1284 ///
1285 /// let scroll: Action = Action::Scroll(
1286 /// ScrollStyle::LinePos(MovePosition::Beginning, 1.into()));
1287 /// assert_eq!(scroll, action!("scroll -s (line-pos -p beginning -c 1)"));
1288 /// ```
1289 ///
1290 /// See the [ScrollStyle] documentation for how to construct each of its variants.
1291 Scroll(ScrollStyle),
1292
1293 /// Lookup the keyword under the cursor.
1294 ///
1295 /// ## Example: Using `action!`
1296 ///
1297 /// ```
1298 /// use editor_types::prelude::*;
1299 /// use editor_types::{action, Action};
1300 ///
1301 /// let kw: Action = Action::KeywordLookup(KeywordTarget::Selection);
1302 /// assert_eq!(kw, action!("keyword-lookup -t selection"));
1303 /// ```
1304 KeywordLookup(KeywordTarget),
1305
1306 /// Redraw the screen.
1307 ///
1308 /// ## Example: Using `action!`
1309 ///
1310 /// ```
1311 /// use editor_types::{action, Action};
1312 ///
1313 /// let redraw: Action = action!("redraw-screen");
1314 /// assert_eq!(redraw, Action::RedrawScreen);
1315 /// ```
1316 RedrawScreen,
1317
1318 /// Show an [InfoMessage].
1319 ShowInfoMessage(InfoMessage),
1320
1321 /// Suspend the process.
1322 ///
1323 /// ## Example: Using `action!`
1324 ///
1325 /// ```
1326 /// use editor_types::{action, Action};
1327 ///
1328 /// let suspend: Action = action!("suspend");
1329 /// assert_eq!(suspend, Action::Suspend);
1330 /// ```
1331 Suspend,
1332
1333 /// Find the [*n*<sup>th</sup>](Count) occurrence of the current application-level search.
1334 ///
1335 /// ## Example: Using `action!`
1336 ///
1337 /// ```
1338 /// use editor_types::prelude::*;
1339 /// use editor_types::{action, Action, CommandBarAction};
1340 ///
1341 /// let search: Action = action!("search -d same");
1342 /// assert_eq!(search, Action::Search(MoveDirMod::Same, Count::Contextual));
1343 /// ```
1344 ///
1345 /// See the documentation for [MoveDirMod] for how to construct all of its values using
1346 /// [action].
1347 Search(MoveDirMod, Count),
1348
1349 /// Perform a command-related action.
1350 ///
1351 /// See the documentation for the [CommandAction] variants for how to construct all of the
1352 /// different [Action::Command] values using [action].
1353 Command(CommandAction),
1354
1355 /// Perform a command bar-related action.
1356 ///
1357 /// See the documentation for the [CommandBarAction] variants for how to construct all of the
1358 /// different [Action::CommandBar] values using [action].
1359 CommandBar(CommandBarAction<I>),
1360
1361 /// Perform a prompt-related action.
1362 ///
1363 /// See the documentation for the [PromptAction] variants for how to construct all of the
1364 /// different [Action::Prompt] values using [action].
1365 Prompt(PromptAction),
1366
1367 /// Perform a tab-related action.
1368 ///
1369 /// See the documentation for the [TabAction] variants for how to construct all of the
1370 /// different [Action::Tab] values using [action].
1371 Tab(TabAction<I>),
1372
1373 /// Perform a window-related action.
1374 ///
1375 /// See the documentation for the [WindowAction] variants for how to construct all of the
1376 /// different [Action::Window] values using [action].
1377 Window(WindowAction<I>),
1378
1379 /// Application-specific command.
1380 Application(I::Action),
1381}
1382
1383impl<I: ApplicationInfo> Action<I> {
1384 /// Indicates how an action gets included in [RepeatType::EditSequence].
1385 ///
1386 /// `motion` indicates what to do with [EditAction::Motion].
1387 pub fn is_edit_sequence(&self, motion: SequenceStatus, ctx: &EditContext) -> SequenceStatus {
1388 match self {
1389 Action::Repeat(_) => SequenceStatus::Ignore,
1390
1391 Action::Application(act) => act.is_edit_sequence(ctx),
1392 Action::Editor(act) => act.is_edit_sequence(motion, ctx),
1393
1394 Action::Command(_) => SequenceStatus::Break,
1395 Action::CommandBar(_) => SequenceStatus::Break,
1396 Action::Jump(_, _, _) => SequenceStatus::Break,
1397 Action::Macro(_) => SequenceStatus::Break,
1398 Action::Prompt(_) => SequenceStatus::Break,
1399 Action::Tab(_) => SequenceStatus::Break,
1400 Action::Window(_) => SequenceStatus::Break,
1401
1402 Action::KeywordLookup(_) => SequenceStatus::Ignore,
1403 Action::NoOp => SequenceStatus::Ignore,
1404 Action::RedrawScreen => SequenceStatus::Ignore,
1405 Action::Scroll(_) => SequenceStatus::Ignore,
1406 Action::Search(_, _) => SequenceStatus::Ignore,
1407 Action::ShowInfoMessage(_) => SequenceStatus::Ignore,
1408 Action::Suspend => SequenceStatus::Ignore,
1409 }
1410 }
1411
1412 /// Indicates how an action gets included in [RepeatType::LastAction].
1413 pub fn is_last_action(&self, ctx: &EditContext) -> SequenceStatus {
1414 match self {
1415 Action::Repeat(RepeatType::EditSequence) => SequenceStatus::Atom,
1416 Action::Repeat(RepeatType::LastAction) => SequenceStatus::Ignore,
1417 Action::Repeat(RepeatType::LastSelection) => SequenceStatus::Atom,
1418
1419 Action::Application(act) => act.is_last_action(ctx),
1420 Action::Editor(act) => act.is_last_action(ctx),
1421
1422 Action::Command(_) => SequenceStatus::Atom,
1423 Action::CommandBar(_) => SequenceStatus::Atom,
1424 Action::Jump(_, _, _) => SequenceStatus::Atom,
1425 Action::Macro(_) => SequenceStatus::Atom,
1426 Action::Tab(_) => SequenceStatus::Atom,
1427 Action::Window(_) => SequenceStatus::Atom,
1428 Action::KeywordLookup(_) => SequenceStatus::Atom,
1429 Action::NoOp => SequenceStatus::Atom,
1430 Action::Prompt(_) => SequenceStatus::Atom,
1431 Action::RedrawScreen => SequenceStatus::Atom,
1432 Action::Scroll(_) => SequenceStatus::Atom,
1433 Action::Search(_, _) => SequenceStatus::Atom,
1434 Action::ShowInfoMessage(_) => SequenceStatus::Atom,
1435 Action::Suspend => SequenceStatus::Atom,
1436 }
1437 }
1438
1439 /// Indicates how an action gets included in [RepeatType::LastSelection].
1440 pub fn is_last_selection(&self, ctx: &EditContext) -> SequenceStatus {
1441 match self {
1442 Action::Repeat(_) => SequenceStatus::Ignore,
1443
1444 Action::Application(act) => act.is_last_selection(ctx),
1445 Action::Editor(act) => act.is_last_selection(ctx),
1446
1447 Action::Command(_) => SequenceStatus::Ignore,
1448 Action::CommandBar(_) => SequenceStatus::Ignore,
1449 Action::Jump(_, _, _) => SequenceStatus::Ignore,
1450 Action::Macro(_) => SequenceStatus::Ignore,
1451 Action::Tab(_) => SequenceStatus::Ignore,
1452 Action::Window(_) => SequenceStatus::Ignore,
1453 Action::KeywordLookup(_) => SequenceStatus::Ignore,
1454 Action::NoOp => SequenceStatus::Ignore,
1455 Action::Prompt(_) => SequenceStatus::Ignore,
1456 Action::RedrawScreen => SequenceStatus::Ignore,
1457 Action::Scroll(_) => SequenceStatus::Ignore,
1458 Action::Search(_, _) => SequenceStatus::Ignore,
1459 Action::ShowInfoMessage(_) => SequenceStatus::Ignore,
1460 Action::Suspend => SequenceStatus::Ignore,
1461 }
1462 }
1463
1464 /// Returns true if this [Action] is allowed to trigger a [WindowAction::Switch] after an error.
1465 pub fn is_switchable(&self, ctx: &EditContext) -> bool {
1466 match self {
1467 Action::Application(act) => act.is_switchable(ctx),
1468 Action::Editor(act) => act.is_switchable(ctx),
1469 Action::Jump(..) => true,
1470
1471 Action::CommandBar(_) => false,
1472 Action::Command(_) => false,
1473 Action::KeywordLookup(_) => false,
1474 Action::Macro(_) => false,
1475 Action::NoOp => false,
1476 Action::Prompt(_) => false,
1477 Action::RedrawScreen => false,
1478 Action::Repeat(_) => false,
1479 Action::Scroll(_) => false,
1480 Action::Search(_, _) => false,
1481 Action::ShowInfoMessage(_) => false,
1482 Action::Suspend => false,
1483 Action::Tab(_) => false,
1484 Action::Window(_) => false,
1485 }
1486 }
1487}
1488
1489#[allow(clippy::derivable_impls)]
1490impl<I: ApplicationInfo> Default for Action<I> {
1491 fn default() -> Self {
1492 Action::NoOp
1493 }
1494}
1495
1496impl<I: ApplicationInfo> From<SelectionAction> for Action<I> {
1497 fn from(act: SelectionAction) -> Self {
1498 Action::Editor(EditorAction::Selection(act))
1499 }
1500}
1501
1502impl<I: ApplicationInfo> From<InsertTextAction> for Action<I> {
1503 fn from(act: InsertTextAction) -> Self {
1504 Action::Editor(EditorAction::InsertText(act))
1505 }
1506}
1507
1508impl<I: ApplicationInfo> From<HistoryAction> for Action<I> {
1509 fn from(act: HistoryAction) -> Self {
1510 Action::Editor(EditorAction::History(act))
1511 }
1512}
1513
1514impl<I: ApplicationInfo> From<CursorAction> for Action<I> {
1515 fn from(act: CursorAction) -> Self {
1516 Action::Editor(EditorAction::Cursor(act))
1517 }
1518}
1519
1520impl<I: ApplicationInfo> From<EditorAction> for Action<I> {
1521 fn from(act: EditorAction) -> Self {
1522 Action::Editor(act)
1523 }
1524}
1525
1526impl<I: ApplicationInfo> From<MacroAction> for Action<I> {
1527 fn from(act: MacroAction) -> Self {
1528 Action::Macro(act)
1529 }
1530}
1531
1532impl<I: ApplicationInfo> From<CommandAction> for Action<I> {
1533 fn from(act: CommandAction) -> Self {
1534 Action::Command(act)
1535 }
1536}
1537
1538impl<I: ApplicationInfo> From<CommandBarAction<I>> for Action<I> {
1539 fn from(act: CommandBarAction<I>) -> Self {
1540 Action::CommandBar(act)
1541 }
1542}
1543
1544impl<I: ApplicationInfo> From<PromptAction> for Action<I> {
1545 fn from(act: PromptAction) -> Self {
1546 Action::Prompt(act)
1547 }
1548}
1549
1550impl<I: ApplicationInfo> From<WindowAction<I>> for Action<I> {
1551 fn from(act: WindowAction<I>) -> Self {
1552 Action::Window(act)
1553 }
1554}
1555
1556impl<I: ApplicationInfo> From<TabAction<I>> for Action<I> {
1557 fn from(act: TabAction<I>) -> Self {
1558 Action::Tab(act)
1559 }
1560}
1561
1562#[cfg(test)]
1563mod tests {
1564 use super::*;
1565
1566 #[test]
1567 fn test_is_readonly() {
1568 let mut ctx = EditContext::default();
1569
1570 let act = SelectionAction::Duplicate(MoveDir1D::Next, Count::Contextual);
1571 assert_eq!(EditorAction::from(act).is_readonly(&ctx), true);
1572
1573 let act = HistoryAction::Checkpoint;
1574 assert_eq!(EditorAction::from(act).is_readonly(&ctx), true);
1575
1576 let act = HistoryAction::Undo(Count::Contextual);
1577 assert_eq!(EditorAction::from(act).is_readonly(&ctx), false);
1578
1579 let act = EditorAction::Edit(Specifier::Contextual, EditTarget::CurrentPosition);
1580 ctx.operation = EditAction::Motion;
1581 assert_eq!(act.is_readonly(&ctx), true);
1582
1583 let act = EditorAction::Edit(Specifier::Contextual, EditTarget::CurrentPosition);
1584 ctx.operation = EditAction::Delete;
1585 assert_eq!(act.is_readonly(&ctx), false);
1586 }
1587}