editor_types/
context.rs

1//! # Editing Context
2//!
3//! ## Overview
4//!
5//! This module contains the contexts used by the editing buffer.
6use crate::prelude::*;
7use crate::EditAction;
8
9/// Trait for values that can be converted by the [EditContext].
10pub trait Resolve<T, R> {
11    /// Use contextual information to convert a `T` into an `R`.
12    fn resolve(&self, t: &T) -> R;
13}
14
15/// Context for editing operations.
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct EditContext {
18    pub(crate) operation: EditAction,
19    pub(crate) mark: Option<Mark>,
20    pub(crate) count: Option<usize>,
21    pub(crate) typed: Option<Char>,
22
23    pub(crate) cursor_end: CursorEnd,
24    pub(crate) target_shape: Option<TargetShape>,
25    pub(crate) insert_style: Option<InsertStyle>,
26    pub(crate) last_column: bool,
27    pub(crate) register: Option<Register>,
28    pub(crate) register_append: bool,
29    pub(crate) search_regex_dir: MoveDir1D,
30    pub(crate) search_char: Option<(MoveDir1D, bool, Char)>,
31    pub(crate) replace_char: Option<Char>,
32    pub(crate) search_incremental: bool,
33}
34
35impl Default for EditContext {
36    fn default() -> Self {
37        Self {
38            operation: EditAction::Motion,
39            mark: None,
40            count: None,
41            typed: None,
42
43            cursor_end: CursorEnd::Auto,
44            target_shape: None,
45            insert_style: None,
46            last_column: true,
47            register: None,
48            register_append: false,
49            search_regex_dir: MoveDir1D::Next,
50            search_char: None,
51            replace_char: None,
52            search_incremental: false,
53        }
54    }
55}
56
57impl EditContext {
58    /// Indicates where to leave the cursor after editing text.
59    pub fn get_cursor_end(&self) -> CursorEnd {
60        self.cursor_end
61    }
62
63    /// Indicates a shape to be applied to an [EditAction].
64    pub fn get_target_shape(&self) -> Option<TargetShape> {
65        self.target_shape
66    }
67
68    /// Indicates the style by which text should be inserted into the buffer.
69    pub fn get_insert_style(&self) -> Option<InsertStyle> {
70        self.insert_style
71    }
72
73    /// Indicates whether it is okay to move the cursor into the last column of a line.
74    pub fn get_last_column(&self) -> bool {
75        self.last_column
76    }
77
78    /// Indicates a count added to the context (if there is one).
79    pub fn get_count(&self) -> Option<usize> {
80        self.count
81    }
82
83    /// Indicates which register yanked and deleted text should go to.
84    pub fn get_register(&self) -> Option<Register> {
85        self.register.clone()
86    }
87
88    /// Indicates whether should be appended to the target register when yanking or deleting text.
89    pub fn get_register_append(&self) -> bool {
90        self.register_append
91    }
92
93    /// Get the direction in which to search.
94    pub fn get_search_regex_dir(&self) -> MoveDir1D {
95        self.search_regex_dir
96    }
97
98    /// Returns a character to search for on the current line, and the direction in
99    /// which to search.
100    pub fn get_search_char(&self) -> Option<(MoveDir1D, bool, Char)> {
101        self.search_char.clone()
102    }
103
104    /// Returns a [character](Char) to use when performing an [EditAction::Replace] operation.
105    pub fn get_replace_char(&self) -> Option<Char> {
106        self.replace_char.clone()
107    }
108
109    /// Whether to perform incremental searches while typing in the search bar.
110    pub fn is_search_incremental(&self) -> bool {
111        self.search_incremental
112    }
113}
114
115impl Resolve<Count, usize> for EditContext {
116    fn resolve(&self, count: &Count) -> usize {
117        match count {
118            Count::Contextual => self.count.unwrap_or(1),
119            Count::MinusOne => self.count.unwrap_or(0).saturating_sub(1),
120            Count::Exact(n) => *n,
121        }
122    }
123}
124
125impl Resolve<Specifier<Char>, Option<Char>> for EditContext {
126    fn resolve(&self, c: &Specifier<Char>) -> Option<Char> {
127        match c {
128            Specifier::Contextual => self.typed.clone(),
129            Specifier::Exact(c) => Some(c.clone()),
130        }
131    }
132}
133
134impl Resolve<Specifier<Mark>, Mark> for EditContext {
135    fn resolve(&self, mark: &Specifier<Mark>) -> Mark {
136        match mark {
137            Specifier::Contextual => self.mark.unwrap_or(Mark::LastJump),
138            Specifier::Exact(m) => *m,
139        }
140    }
141}
142
143impl Resolve<Specifier<EditAction>, EditAction> for EditContext {
144    fn resolve(&self, mark: &Specifier<EditAction>) -> EditAction {
145        match mark {
146            Specifier::Contextual => self.operation.clone(),
147            Specifier::Exact(a) => a.clone(),
148        }
149    }
150}
151
152/// Build a new [EditContext].
153#[derive(Default)]
154pub struct EditContextBuilder(EditContext);
155
156impl EditContextBuilder {
157    /// Finish building the [EditContext].
158    pub fn build(self) -> EditContext {
159        self.0
160    }
161
162    /// Set the [CursorEnd].
163    ///
164    /// Defaults to [CursorEnd::Auto].
165    pub fn cursor_end(mut self, v: CursorEnd) -> Self {
166        self.0.cursor_end = v;
167        self
168    }
169
170    /// Set the [TargetShape].
171    ///
172    /// Defaults to [None].
173    pub fn target_shape(mut self, v: Option<TargetShape>) -> Self {
174        self.0.target_shape = v;
175        self
176    }
177
178    /// Set the [InsertStyle].
179    ///
180    /// Defaults to [None].
181    pub fn insert_style(mut self, v: Option<InsertStyle>) -> Self {
182        self.0.insert_style = v;
183        self
184    }
185
186    /// Set whether it's okay to move the cursor into the last column.
187    ///
188    /// Defaults to [true].
189    pub fn last_column(mut self, v: bool) -> Self {
190        self.0.last_column = v;
191        self
192    }
193
194    /// Set the [Register].
195    ///
196    /// Defaults to [None].
197    pub fn register(mut self, v: Option<Register>) -> Self {
198        self.0.register = v;
199        self
200    }
201
202    /// Set whether this operation should append contents to the register or replace the existing
203    /// ones.
204    ///
205    /// Defaults to [false].
206    pub fn register_append(mut self, v: bool) -> Self {
207        self.0.register_append = v;
208        self
209    }
210
211    /// Set the direction the regular expression should search in.
212    ///
213    /// Defaults to [MoveDir1D::Next].
214    pub fn search_regex_dir(mut self, v: MoveDir1D) -> Self {
215        self.0.search_regex_dir = v;
216        self
217    }
218
219    /// Set a character to search for.
220    ///
221    /// Defaults to [None].
222    pub fn search_char(mut self, v: Option<(MoveDir1D, bool, Char)>) -> Self {
223        self.0.search_char = v;
224        self
225    }
226
227    /// Set a [Char] to replace existing characters with.
228    ///
229    /// Defaults to [None].
230    pub fn replace_char(mut self, v: Option<Char>) -> Self {
231        self.0.replace_char = v;
232        self
233    }
234
235    /// Set whether we should do an incremental search.
236    ///
237    /// Defaults to [false].
238    pub fn search_incremental(mut self, v: bool) -> Self {
239        self.0.search_incremental = v;
240        self
241    }
242
243    /// Set the contextual [Mark].
244    ///
245    /// Defaults to [None].
246    pub fn mark(mut self, mark: Option<Mark>) -> Self {
247        self.0.mark = mark;
248        self
249    }
250
251    /// Set the contextual [Mark].
252    ///
253    /// Defaults to [None].
254    pub fn typed_char(mut self, ch: Option<Char>) -> Self {
255        self.0.typed = ch;
256        self
257    }
258
259    /// Set the contextual [Mark].
260    ///
261    /// Defaults to [EditAction::Motion].
262    pub fn operation(mut self, op: EditAction) -> Self {
263        self.0.operation = op;
264        self
265    }
266
267    /// Set the contextual count.
268    ///
269    /// Defaults to [None].
270    pub fn count(mut self, n: Option<usize>) -> Self {
271        self.0.count = n;
272        self
273    }
274}
275
276impl From<EditContext> for EditContextBuilder {
277    fn from(ctx: EditContext) -> Self {
278        EditContextBuilder(ctx)
279    }
280}