tui-canvas 0.8.10

Form/textarea/input for TUI
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
use std::{convert::Infallible, fmt, str::FromStr};

use crate::canvas::actions::CanvasAction;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CanvasKeyAction {
    MoveLeft,
    MoveRight,
    MoveUp,
    MoveDown,
    NextField,
    PrevField,
    MoveLineStart,
    MoveLineEnd,
    MoveHalfPageUp,
    MoveHalfPageDown,
    MoveFirstLine,
    MoveLastLine,
    MoveWordNext,
    MoveWordPrev,
    MoveWordEnd,
    MoveWordEndPrev,
    MoveBigWordNext,
    MoveBigWordPrev,
    MoveBigWordEnd,
    MoveBigWordEndPrev,
    DeleteCharBackward,
    DeleteCharForward,
    Undo,
    Redo,
    OpenLineBelow,
    OpenLineAbove,
    EnterEditModeLineStart,
    EnterEditModeLineEnd,
    DeleteLine,
    DeleteToLineEnd,
    ChangeLine,
    ChangeToLineEnd,
    OperatorDelete,
    OperatorChange,
    OperatorYank,
    JoinLineBelow,
    YankLine,
    PasteAfter,
    PasteBefore,
    OpenSuggestions,
    ApplySuggestion,
    ExitSuggestions,
    EnterDecider,
    SuggestionDown,
    SuggestionUp,
    EnterEditModeBefore,
    EnterEditModeAfter,
    Exit,
    ExitEditMode,
    EnterHighlightMode,
    EnterHighlightModeLinewise,
    ExitHighlightMode,
    DeleteSelection,
    DeleteSelectionNoYank,
    ChangeSelection,
    ChangeSelectionNoYank,
    YankSelection,
    CollapseSelection,
    ExtendLineBelow,
    ExtendToLineBounds,
    SearchNext,
    SearchPrev,
    SelectAll,
    FlipSelections,
    SwitchCase,
    SwitchToLowercase,
    SwitchToUppercase,
    TrimSelections,
    GotoFirstNonWhitespace,
    MovePageUp,
    MovePageDown,
    SearchSelection,
    EnsureSelectionForward,
    MatchBrackets,
    IndentSelection,
    UnindentSelection,
    IncrementNumber,
    DecrementNumber,
    FindNextChar,
    FindPrevChar,
    TillNextChar,
    TillPrevChar,
    ReplaceChar,
    RepeatLastFind,
    RepeatLastFindReverse,
    SurroundAdd,
    SurroundDelete,
    SurroundReplace,
    DeleteWordBackward,
    DeleteToLineStart,
    DeleteWordForward,
    ClearSearch,
    // VSCode-style line operations (paradigm-agnostic; see textarea line ops).
    MoveLineUp,
    MoveLineDown,
    DuplicateLineUp,
    DuplicateLineDown,
    CopyLine,
    CutLine,
    // VSCode-style Shift+movement selection extension (modeless).
    SelectLeft,
    SelectRight,
    SelectUp,
    SelectDown,
    SelectWordPrev,
    SelectWordNext,
    SelectLineStart,
    SelectLineEnd,
    SelectDocStart,
    SelectDocEnd,
    Unknown(String),
}

impl CanvasKeyAction {
    pub fn from_name(name: &str) -> Self {
        match name {
            "move_left" => Self::MoveLeft,
            "move_right" => Self::MoveRight,
            "move_up" => Self::MoveUp,
            "move_down" => Self::MoveDown,
            "next_field" => Self::NextField,
            "prev_field" => Self::PrevField,
            "move_line_start" => Self::MoveLineStart,
            "move_line_end" => Self::MoveLineEnd,
            "move_half_page_up" => Self::MoveHalfPageUp,
            "move_half_page_down" => Self::MoveHalfPageDown,
            "move_first_line" => Self::MoveFirstLine,
            "move_last_line" => Self::MoveLastLine,
            "move_word_next" => Self::MoveWordNext,
            "move_word_prev" => Self::MoveWordPrev,
            "move_word_end" => Self::MoveWordEnd,
            "move_word_end_prev" => Self::MoveWordEndPrev,
            "move_big_word_next" => Self::MoveBigWordNext,
            "move_big_word_prev" => Self::MoveBigWordPrev,
            "move_big_word_end" => Self::MoveBigWordEnd,
            "move_big_word_end_prev" => Self::MoveBigWordEndPrev,
            "delete_char_backward" => Self::DeleteCharBackward,
            "delete_char_forward" => Self::DeleteCharForward,
            "undo" => Self::Undo,
            "redo" => Self::Redo,
            "open_line_below" => Self::OpenLineBelow,
            "open_line_above" => Self::OpenLineAbove,
            "enter_edit_mode_line_start" => Self::EnterEditModeLineStart,
            "enter_edit_mode_line_end" => Self::EnterEditModeLineEnd,
            "delete_line" => Self::DeleteLine,
            "delete_to_line_end" => Self::DeleteToLineEnd,
            "change_line" => Self::ChangeLine,
            "change_to_line_end" => Self::ChangeToLineEnd,
            "operator_delete" => Self::OperatorDelete,
            "operator_change" => Self::OperatorChange,
            "operator_yank" => Self::OperatorYank,
            "join_line_below" => Self::JoinLineBelow,
            "yank_line" => Self::YankLine,
            "paste_after" => Self::PasteAfter,
            "paste_before" => Self::PasteBefore,
            "open_suggestions" => Self::OpenSuggestions,
            "apply_suggestion" => Self::ApplySuggestion,
            "exit_suggestions" => Self::ExitSuggestions,
            "enter_decider" => Self::EnterDecider,
            "suggestion_down" => Self::SuggestionDown,
            "suggestion_up" => Self::SuggestionUp,
            "enter_edit_mode_before" => Self::EnterEditModeBefore,
            "enter_edit_mode_after" => Self::EnterEditModeAfter,
            "exit" => Self::Exit,
            "exit_edit_mode" => Self::ExitEditMode,
            "enter_highlight_mode" => Self::EnterHighlightMode,
            "enter_highlight_mode_linewise" => Self::EnterHighlightModeLinewise,
            "exit_highlight_mode" => Self::ExitHighlightMode,
            "delete_selection" => Self::DeleteSelection,
            "delete_selection_noyank" => Self::DeleteSelectionNoYank,
            "change_selection" => Self::ChangeSelection,
            "change_selection_noyank" => Self::ChangeSelectionNoYank,
            "yank_selection" => Self::YankSelection,
            "collapse_selection" => Self::CollapseSelection,
            "extend_line_below" => Self::ExtendLineBelow,
            "extend_to_line_bounds" => Self::ExtendToLineBounds,
            "search_next" => Self::SearchNext,
            "search_prev" => Self::SearchPrev,
            "select_all" => Self::SelectAll,
            "flip_selections" => Self::FlipSelections,
            "switch_case" => Self::SwitchCase,
            "switch_to_lowercase" => Self::SwitchToLowercase,
            "switch_to_uppercase" => Self::SwitchToUppercase,
            "trim_selections" => Self::TrimSelections,
            "goto_first_nonwhitespace" => Self::GotoFirstNonWhitespace,
            "move_page_up" => Self::MovePageUp,
            "move_page_down" => Self::MovePageDown,
            "search_selection" => Self::SearchSelection,
            "ensure_selection_forward" => Self::EnsureSelectionForward,
            "match_brackets" => Self::MatchBrackets,
            "indent_selection" => Self::IndentSelection,
            "unindent_selection" => Self::UnindentSelection,
            "increment_number" => Self::IncrementNumber,
            "decrement_number" => Self::DecrementNumber,
            "find_next_char" => Self::FindNextChar,
            "find_prev_char" => Self::FindPrevChar,
            "till_next_char" => Self::TillNextChar,
            "till_prev_char" => Self::TillPrevChar,
            "replace_char" => Self::ReplaceChar,
            "repeat_last_find" => Self::RepeatLastFind,
            "repeat_last_find_reverse" => Self::RepeatLastFindReverse,
            "surround_add" => Self::SurroundAdd,
            "surround_delete" => Self::SurroundDelete,
            "surround_replace" => Self::SurroundReplace,
            "delete_word_backward" => Self::DeleteWordBackward,
            "delete_to_line_start" => Self::DeleteToLineStart,
            "delete_word_forward" => Self::DeleteWordForward,
            "clear_search" => Self::ClearSearch,
            "move_line_up" => Self::MoveLineUp,
            "move_line_down" => Self::MoveLineDown,
            "duplicate_line_up" => Self::DuplicateLineUp,
            "duplicate_line_down" => Self::DuplicateLineDown,
            "copy_line" => Self::CopyLine,
            "cut_line" => Self::CutLine,
            "select_left" => Self::SelectLeft,
            "select_right" => Self::SelectRight,
            "select_up" => Self::SelectUp,
            "select_down" => Self::SelectDown,
            "select_word_prev" => Self::SelectWordPrev,
            "select_word_next" => Self::SelectWordNext,
            "select_line_start" => Self::SelectLineStart,
            "select_line_end" => Self::SelectLineEnd,
            "select_doc_start" => Self::SelectDocStart,
            "select_doc_end" => Self::SelectDocEnd,
            other => Self::Unknown(other.to_string()),
        }
    }

    pub fn as_str(&self) -> &str {
        match self {
            Self::MoveLeft => "move_left",
            Self::MoveRight => "move_right",
            Self::MoveUp => "move_up",
            Self::MoveDown => "move_down",
            Self::NextField => "next_field",
            Self::PrevField => "prev_field",
            Self::MoveLineStart => "move_line_start",
            Self::MoveLineEnd => "move_line_end",
            Self::MoveHalfPageUp => "move_half_page_up",
            Self::MoveHalfPageDown => "move_half_page_down",
            Self::MoveFirstLine => "move_first_line",
            Self::MoveLastLine => "move_last_line",
            Self::MoveWordNext => "move_word_next",
            Self::MoveWordPrev => "move_word_prev",
            Self::MoveWordEnd => "move_word_end",
            Self::MoveWordEndPrev => "move_word_end_prev",
            Self::MoveBigWordNext => "move_big_word_next",
            Self::MoveBigWordPrev => "move_big_word_prev",
            Self::MoveBigWordEnd => "move_big_word_end",
            Self::MoveBigWordEndPrev => "move_big_word_end_prev",
            Self::DeleteCharBackward => "delete_char_backward",
            Self::DeleteCharForward => "delete_char_forward",
            Self::Undo => "undo",
            Self::Redo => "redo",
            Self::OpenLineBelow => "open_line_below",
            Self::OpenLineAbove => "open_line_above",
            Self::EnterEditModeLineStart => "enter_edit_mode_line_start",
            Self::EnterEditModeLineEnd => "enter_edit_mode_line_end",
            Self::DeleteLine => "delete_line",
            Self::DeleteToLineEnd => "delete_to_line_end",
            Self::ChangeLine => "change_line",
            Self::ChangeToLineEnd => "change_to_line_end",
            Self::OperatorDelete => "operator_delete",
            Self::OperatorChange => "operator_change",
            Self::OperatorYank => "operator_yank",
            Self::JoinLineBelow => "join_line_below",
            Self::YankLine => "yank_line",
            Self::PasteAfter => "paste_after",
            Self::PasteBefore => "paste_before",
            Self::OpenSuggestions => "open_suggestions",
            Self::ApplySuggestion => "apply_suggestion",
            Self::ExitSuggestions => "exit_suggestions",
            Self::EnterDecider => "enter_decider",
            Self::SuggestionDown => "suggestion_down",
            Self::SuggestionUp => "suggestion_up",
            Self::EnterEditModeBefore => "enter_edit_mode_before",
            Self::EnterEditModeAfter => "enter_edit_mode_after",
            Self::Exit => "exit",
            Self::ExitEditMode => "exit_edit_mode",
            Self::EnterHighlightMode => "enter_highlight_mode",
            Self::EnterHighlightModeLinewise => "enter_highlight_mode_linewise",
            Self::ExitHighlightMode => "exit_highlight_mode",
            Self::DeleteSelection => "delete_selection",
            Self::DeleteSelectionNoYank => "delete_selection_noyank",
            Self::ChangeSelection => "change_selection",
            Self::ChangeSelectionNoYank => "change_selection_noyank",
            Self::YankSelection => "yank_selection",
            Self::CollapseSelection => "collapse_selection",
            Self::SearchNext => "search_next",
            Self::SearchPrev => "search_prev",
            Self::SelectAll => "select_all",
            Self::FlipSelections => "flip_selections",
            Self::SwitchCase => "switch_case",
            Self::SwitchToLowercase => "switch_to_lowercase",
            Self::SwitchToUppercase => "switch_to_uppercase",
            Self::TrimSelections => "trim_selections",
            Self::GotoFirstNonWhitespace => "goto_first_nonwhitespace",
            Self::MovePageUp => "move_page_up",
            Self::MovePageDown => "move_page_down",
            Self::SearchSelection => "search_selection",
            Self::EnsureSelectionForward => "ensure_selection_forward",
            Self::MatchBrackets => "match_brackets",
            Self::IndentSelection => "indent_selection",
            Self::UnindentSelection => "unindent_selection",
            Self::IncrementNumber => "increment_number",
            Self::DecrementNumber => "decrement_number",
            Self::FindNextChar => "find_next_char",
            Self::FindPrevChar => "find_prev_char",
            Self::TillNextChar => "till_next_char",
            Self::TillPrevChar => "till_prev_char",
            Self::ReplaceChar => "replace_char",
            Self::RepeatLastFind => "repeat_last_find",
            Self::RepeatLastFindReverse => "repeat_last_find_reverse",
            Self::SurroundAdd => "surround_add",
            Self::SurroundDelete => "surround_delete",
            Self::SurroundReplace => "surround_replace",
            Self::DeleteWordBackward => "delete_word_backward",
            Self::DeleteToLineStart => "delete_to_line_start",
            Self::DeleteWordForward => "delete_word_forward",
            Self::ClearSearch => "clear_search",
            Self::MoveLineUp => "move_line_up",
            Self::MoveLineDown => "move_line_down",
            Self::DuplicateLineUp => "duplicate_line_up",
            Self::DuplicateLineDown => "duplicate_line_down",
            Self::CopyLine => "copy_line",
            Self::CutLine => "cut_line",
            Self::SelectLeft => "select_left",
            Self::SelectRight => "select_right",
            Self::SelectUp => "select_up",
            Self::SelectDown => "select_down",
            Self::SelectWordPrev => "select_word_prev",
            Self::SelectWordNext => "select_word_next",
            Self::SelectLineStart => "select_line_start",
            Self::SelectLineEnd => "select_line_end",
            Self::SelectDocStart => "select_doc_start",
            Self::SelectDocEnd => "select_doc_end",
            Self::ExtendLineBelow => "extend_line_below",
            Self::ExtendToLineBounds => "extend_to_line_bounds",
            Self::Unknown(other) => other.as_str(),
        }
    }

    pub fn to_canvas_action(&self) -> Option<CanvasAction> {
        Some(match self {
            Self::MoveLeft => CanvasAction::MoveLeft,
            Self::MoveRight => CanvasAction::MoveRight,
            Self::MoveUp => CanvasAction::MoveUp,
            Self::MoveDown => CanvasAction::MoveDown,
            Self::MoveWordNext => CanvasAction::MoveWordNext,
            Self::MoveWordPrev => CanvasAction::MoveWordPrev,
            Self::MoveWordEnd => CanvasAction::MoveWordEnd,
            Self::MoveWordEndPrev => CanvasAction::MoveWordEndPrev,
            Self::MoveBigWordNext => CanvasAction::MoveBigWordNext,
            Self::MoveBigWordPrev => CanvasAction::MoveBigWordPrev,
            Self::MoveBigWordEnd => CanvasAction::MoveBigWordEnd,
            Self::MoveBigWordEndPrev => CanvasAction::MoveBigWordEndPrev,
            Self::MoveLineStart => CanvasAction::MoveLineStart,
            Self::MoveLineEnd => CanvasAction::MoveLineEnd,
            Self::MoveHalfPageUp | Self::MoveHalfPageDown => return None,
            Self::NextField => CanvasAction::NextField,
            Self::PrevField => CanvasAction::PrevField,
            Self::MoveFirstLine => CanvasAction::MoveFirstLine,
            Self::MoveLastLine => CanvasAction::MoveLastLine,
            Self::DeleteCharBackward => CanvasAction::DeleteBackward,
            Self::DeleteCharForward => CanvasAction::DeleteForward,
            Self::Undo => CanvasAction::Undo,
            Self::Redo => CanvasAction::Redo,
            Self::OpenSuggestions => CanvasAction::TriggerSuggestions,
            Self::SuggestionUp => CanvasAction::SuggestionUp,
            Self::SuggestionDown => CanvasAction::SuggestionDown,
            Self::ApplySuggestion => CanvasAction::SelectSuggestion,
            Self::ExitSuggestions => CanvasAction::ExitSuggestions,
            Self::EnterEditModeBefore => CanvasAction::EnterEditMode,
            Self::EnterEditModeAfter => CanvasAction::EnterEditModeAfter,
            Self::ExitEditMode => CanvasAction::ExitEditMode,
            Self::EnterHighlightMode => CanvasAction::EnterHighlightMode,
            Self::EnterHighlightModeLinewise => CanvasAction::EnterHighlightModeLinewise,
            Self::ExitHighlightMode => CanvasAction::ExitHighlightMode,
            Self::OpenLineBelow => CanvasAction::OpenLineBelow,
            Self::OpenLineAbove => CanvasAction::OpenLineAbove,
            Self::EnterEditModeLineStart
            | Self::EnterEditModeLineEnd
            | Self::DeleteLine
            | Self::DeleteToLineEnd
            | Self::ChangeLine
            | Self::ChangeToLineEnd
            | Self::OperatorDelete
            | Self::OperatorChange
            | Self::OperatorYank
            | Self::JoinLineBelow
            | Self::YankLine
            | Self::PasteAfter
            | Self::PasteBefore
            | Self::DeleteSelection
            | Self::DeleteSelectionNoYank
            | Self::ChangeSelection
            | Self::ChangeSelectionNoYank
            | Self::YankSelection
            | Self::CollapseSelection
            | Self::ExtendLineBelow
            | Self::ExtendToLineBounds
            | Self::SearchNext
            | Self::SearchPrev
            | Self::SelectAll
            | Self::FlipSelections
            | Self::SwitchCase
            | Self::SwitchToLowercase
            | Self::SwitchToUppercase
            | Self::TrimSelections
            | Self::GotoFirstNonWhitespace
            | Self::MovePageUp
            | Self::MovePageDown
            | Self::SearchSelection
            | Self::EnsureSelectionForward
            | Self::MatchBrackets
            | Self::IndentSelection
            | Self::UnindentSelection
            | Self::IncrementNumber
            | Self::DecrementNumber
            | Self::FindNextChar
            | Self::FindPrevChar
            | Self::TillNextChar
            | Self::TillPrevChar
            | Self::ReplaceChar
            | Self::RepeatLastFind
            | Self::RepeatLastFindReverse
            | Self::SurroundAdd
            | Self::SurroundDelete
            | Self::SurroundReplace
            | Self::DeleteWordBackward
            | Self::DeleteToLineStart
            | Self::DeleteWordForward
            | Self::ClearSearch
            | Self::MoveLineUp
            | Self::MoveLineDown
            | Self::DuplicateLineUp
            | Self::DuplicateLineDown
            | Self::CopyLine
            | Self::CutLine
            | Self::SelectLeft
            | Self::SelectRight
            | Self::SelectUp
            | Self::SelectDown
            | Self::SelectWordPrev
            | Self::SelectWordNext
            | Self::SelectLineStart
            | Self::SelectLineEnd
            | Self::SelectDocStart
            | Self::SelectDocEnd => return None,
            Self::EnterDecider | Self::Exit | Self::Unknown(_) => return None,
        })
    }
}

impl FromStr for CanvasKeyAction {
    type Err = Infallible;

    fn from_str(name: &str) -> Result<Self, Self::Err> {
        Ok(Self::from_name(name))
    }
}

impl fmt::Display for CanvasKeyAction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl serde::Serialize for CanvasKeyAction {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(self.as_str())
    }
}

impl<'de> serde::Deserialize<'de> for CanvasKeyAction {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let name = <String as serde::Deserialize>::deserialize(deserializer)?;
        Ok(Self::from_name(&name))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn action_names_round_trip_through_traits_and_serde() {
        assert_eq!("undo".parse::<CanvasKeyAction>(), Ok(CanvasKeyAction::Undo));
        assert_eq!(CanvasKeyAction::Redo.to_string(), "redo");
        assert_eq!(
            toml::from_str::<ActionConfig>("action = \"custom_action\"")
                .unwrap()
                .action,
            CanvasKeyAction::Unknown("custom_action".to_string())
        );
        assert_eq!(
            toml::to_string(&ActionConfig {
                action: CanvasKeyAction::MoveLeft,
            })
            .unwrap(),
            "action = \"move_left\"\n"
        );
    }

    #[derive(Debug, serde::Deserialize, serde::Serialize)]
    struct ActionConfig {
        action: CanvasKeyAction,
    }
}