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