1 use std::sync::{Arc, Mutex};
2 
3 use crate::complete_hint_line;
4 use crate::config::Config;
5 use crate::edit::State;
6 use crate::error;
7 use crate::history::Direction;
8 use crate::keymap::{Anchor, At, Cmd, Movement, Word};
9 use crate::keymap::{InputState, Refresher};
10 use crate::kill_ring::{KillRing, Mode};
11 use crate::line_buffer::WordAction;
12 use crate::{Helper, Result};
13 
14 pub enum Status {
15     Proceed,
16     Submit,
17 }
18 
execute<H: Helper>( cmd: Cmd, s: &mut State<'_, '_, H>, input_state: &InputState, kill_ring: &Arc<Mutex<KillRing>>, config: &Config, ) -> Result<Status>19 pub fn execute<H: Helper>(
20     cmd: Cmd,
21     s: &mut State<'_, '_, H>,
22     input_state: &InputState,
23     kill_ring: &Arc<Mutex<KillRing>>,
24     config: &Config,
25 ) -> Result<Status> {
26     use Status::*;
27 
28     match cmd {
29         Cmd::CompleteHint => {
30             complete_hint_line(s)?;
31         }
32         Cmd::SelfInsert(n, c) => {
33             s.edit_insert(c, n)?;
34         }
35         Cmd::Insert(n, text) => {
36             s.edit_yank(&input_state, &text, Anchor::Before, n)?;
37         }
38         Cmd::Move(Movement::BeginningOfLine) => {
39             // Move to the beginning of line.
40             s.edit_move_home()?
41         }
42         Cmd::Move(Movement::ViFirstPrint) => {
43             s.edit_move_home()?;
44             s.edit_move_to_next_word(At::Start, Word::Big, 1)?
45         }
46         Cmd::Move(Movement::BackwardChar(n)) => {
47             // Move back a character.
48             s.edit_move_backward(n)?
49         }
50         Cmd::ReplaceChar(n, c) => s.edit_replace_char(c, n)?,
51         Cmd::Replace(mvt, text) => {
52             s.edit_kill(&mvt)?;
53             if let Some(text) = text {
54                 s.edit_insert_text(&text)?
55             }
56         }
57         Cmd::Overwrite(c) => {
58             s.edit_overwrite_char(c)?;
59         }
60         Cmd::EndOfFile => {
61             if input_state.is_emacs_mode() && !s.line.is_empty() {
62                 s.edit_delete(1)?
63             } else {
64                 if s.has_hint() || !s.is_default_prompt() {
65                     // Force a refresh without hints to leave the previous
66                     // line as the user typed it after a newline.
67                     s.refresh_line_with_msg(None)?;
68                 }
69                 if s.line.is_empty() {
70                     return Err(error::ReadlineError::Eof);
71                 } else if !input_state.is_emacs_mode() {
72                     return Ok(Submit);
73                 }
74             }
75         }
76         Cmd::Move(Movement::EndOfLine) => {
77             // Move to the end of line.
78             s.edit_move_end()?
79         }
80         Cmd::Move(Movement::ForwardChar(n)) => {
81             // Move forward a character.
82             s.edit_move_forward(n)?
83         }
84         Cmd::ClearScreen => {
85             // Clear the screen leaving the current line at the top of the screen.
86             s.clear_screen()?;
87             s.refresh_line()?
88         }
89         Cmd::NextHistory => {
90             // Fetch the next command from the history list.
91             s.edit_history_next(false)?
92         }
93         Cmd::PreviousHistory => {
94             // Fetch the previous command from the history list.
95             s.edit_history_next(true)?
96         }
97         Cmd::LineUpOrPreviousHistory(n) => {
98             if !s.edit_move_line_up(n)? {
99                 s.edit_history_next(true)?
100             }
101         }
102         Cmd::LineDownOrNextHistory(n) => {
103             if !s.edit_move_line_down(n)? {
104                 s.edit_history_next(false)?
105             }
106         }
107         Cmd::HistorySearchBackward => s.edit_history_search(Direction::Reverse)?,
108         Cmd::HistorySearchForward => s.edit_history_search(Direction::Forward)?,
109         Cmd::TransposeChars => {
110             // Exchange the char before cursor with the character at cursor.
111             s.edit_transpose_chars()?
112         }
113         Cmd::Yank(n, anchor) => {
114             // retrieve (yank) last item killed
115             let mut kill_ring = kill_ring.lock().unwrap();
116             if let Some(text) = kill_ring.yank() {
117                 s.edit_yank(&input_state, text, anchor, n)?
118             }
119         }
120         Cmd::ViYankTo(ref mvt) => {
121             if let Some(text) = s.line.copy(mvt) {
122                 let mut kill_ring = kill_ring.lock().unwrap();
123                 kill_ring.kill(&text, Mode::Append)
124             }
125         }
126         Cmd::AcceptLine | Cmd::AcceptOrInsertLine { .. } | Cmd::Newline => {
127             if s.has_hint() || !s.is_default_prompt() {
128                 // Force a refresh without hints to leave the previous
129                 // line as the user typed it after a newline.
130                 s.refresh_line_with_msg(None)?;
131             }
132             let validation_result = s.validate()?;
133             let valid = validation_result.is_valid();
134             let end = s.line.is_end_of_input();
135             match (cmd, valid, end) {
136                 (Cmd::AcceptLine, ..)
137                 | (Cmd::AcceptOrInsertLine { .. }, true, true)
138                 | (
139                     Cmd::AcceptOrInsertLine {
140                         accept_in_the_middle: true,
141                     },
142                     true,
143                     _,
144                 ) => {
145                     return Ok(Submit);
146                 }
147                 (Cmd::Newline, ..)
148                 | (Cmd::AcceptOrInsertLine { .. }, false, _)
149                 | (Cmd::AcceptOrInsertLine { .. }, true, false) => {
150                     if valid || !validation_result.has_message() {
151                         s.edit_insert('\n', 1)?;
152                     }
153                 }
154                 _ => unreachable!(),
155             }
156         }
157         Cmd::BeginningOfHistory => {
158             // move to first entry in history
159             s.edit_history(true)?
160         }
161         Cmd::EndOfHistory => {
162             // move to last entry in history
163             s.edit_history(false)?
164         }
165         Cmd::Move(Movement::BackwardWord(n, word_def)) => {
166             // move backwards one word
167             s.edit_move_to_prev_word(word_def, n)?
168         }
169         Cmd::CapitalizeWord => {
170             // capitalize word after point
171             s.edit_word(WordAction::CAPITALIZE)?
172         }
173         Cmd::Kill(ref mvt) => {
174             s.edit_kill(mvt)?;
175         }
176         Cmd::Move(Movement::ForwardWord(n, at, word_def)) => {
177             // move forwards one word
178             s.edit_move_to_next_word(at, word_def, n)?
179         }
180         Cmd::Move(Movement::LineUp(n)) => {
181             s.edit_move_line_up(n)?;
182         }
183         Cmd::Move(Movement::LineDown(n)) => {
184             s.edit_move_line_down(n)?;
185         }
186         Cmd::Move(Movement::BeginningOfBuffer) => {
187             // Move to the start of the buffer.
188             s.edit_move_buffer_start()?
189         }
190         Cmd::Move(Movement::EndOfBuffer) => {
191             // Move to the end of the buffer.
192             s.edit_move_buffer_end()?
193         }
194         Cmd::DowncaseWord => {
195             // lowercase word after point
196             s.edit_word(WordAction::LOWERCASE)?
197         }
198         Cmd::TransposeWords(n) => {
199             // transpose words
200             s.edit_transpose_words(n)?
201         }
202         Cmd::UpcaseWord => {
203             // uppercase word after point
204             s.edit_word(WordAction::UPPERCASE)?
205         }
206         Cmd::YankPop => {
207             // yank-pop
208             let mut kill_ring = kill_ring.lock().unwrap();
209             if let Some((yank_size, text)) = kill_ring.yank_pop() {
210                 s.edit_yank_pop(yank_size, text)?
211             }
212         }
213         Cmd::Move(Movement::ViCharSearch(n, cs)) => s.edit_move_to(cs, n)?,
214         Cmd::Undo(n) => {
215             if s.changes.borrow_mut().undo(&mut s.line, n) {
216                 s.refresh_line()?;
217             }
218         }
219         Cmd::Dedent(mvt) => {
220             s.edit_indent(&mvt, config.indent_size(), true)?;
221         }
222         Cmd::Indent(mvt) => {
223             s.edit_indent(&mvt, config.indent_size(), false)?;
224         }
225         Cmd::Interrupt => {
226             // Move to end, in case cursor was in the middle of the
227             // line, so that next thing application prints goes after
228             // the input
229             s.edit_move_buffer_end()?;
230             return Err(error::ReadlineError::Interrupted);
231         }
232         _ => {
233             // Ignore the character typed.
234         }
235     }
236     Ok(Proceed)
237 }
238