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