rustyline/
command.rs

1use std::sync::{Arc, Mutex};
2
3use crate::complete_hint_line;
4use crate::config::Config;
5use crate::edit::State;
6use crate::error;
7use crate::history::SearchDirection;
8use crate::keymap::{Anchor, At, Cmd, Movement, Word};
9use crate::keymap::{InputState, Refresher};
10use crate::kill_ring::{KillRing, Mode};
11use crate::line_buffer::WordAction;
12use crate::{Helper, Result};
13
14pub enum Status {
15    Proceed,
16    Submit,
17}
18
19pub 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::{Proceed, Submit};
27
28    match cmd {
29        Cmd::EndOfFile | Cmd::AcceptLine | Cmd::AcceptOrInsertLine { .. } | Cmd::Newline => {
30            if s.has_hint() || !s.is_default_prompt() {
31                // Force a refresh without hints to leave the previous
32                // line as the user typed it after a newline.
33                s.refresh_line_with_msg(None)?;
34            }
35        }
36        _ => {}
37    };
38    match cmd {
39        Cmd::CompleteHint => {
40            complete_hint_line(s)?;
41        }
42        Cmd::SelfInsert(n, c) => {
43            s.edit_insert(c, n)?;
44        }
45        Cmd::Insert(n, text) => {
46            s.edit_yank(input_state, &text, Anchor::Before, n)?;
47        }
48        Cmd::Move(Movement::BeginningOfLine) => {
49            // Move to the beginning of line.
50            s.edit_move_home()?;
51        }
52        Cmd::Move(Movement::ViFirstPrint) => {
53            s.edit_move_home()?;
54            if s.line.starts_with(char::is_whitespace) {
55                s.edit_move_to_next_word(At::Start, Word::Big, 1)?;
56            }
57        }
58        Cmd::Move(Movement::BackwardChar(n)) => {
59            // Move back a character.
60            s.edit_move_backward(n)?;
61        }
62        Cmd::ReplaceChar(n, c) => s.edit_replace_char(c, n)?,
63        Cmd::Replace(mvt, text) => {
64            s.edit_kill(&mvt)?;
65            if let Some(text) = text {
66                s.edit_insert_text(&text)?;
67            }
68        }
69        Cmd::Overwrite(c) => {
70            s.edit_overwrite_char(c)?;
71        }
72        Cmd::EndOfFile => {
73            if s.line.is_empty() {
74                return Err(error::ReadlineError::Eof);
75            } else if !input_state.is_emacs_mode() {
76                return Ok(Submit);
77            }
78        }
79        Cmd::Move(Movement::EndOfLine) => {
80            // Move to the end of line.
81            s.edit_move_end()?;
82        }
83        Cmd::Move(Movement::ForwardChar(n)) => {
84            // Move forward a character.
85            s.edit_move_forward(n)?;
86        }
87        Cmd::ClearScreen => {
88            // Clear the screen leaving the current line at the top of the screen.
89            s.clear_screen()?;
90            s.refresh_line()?;
91        }
92        Cmd::NextHistory => {
93            // Fetch the next command from the history list.
94            s.edit_history_next(false)?;
95        }
96        Cmd::PreviousHistory => {
97            // Fetch the previous command from the history list.
98            s.edit_history_next(true)?;
99        }
100        Cmd::LineUpOrPreviousHistory(n) => {
101            if !s.edit_move_line_up(n)? {
102                s.edit_history_next(true)?;
103            }
104        }
105        Cmd::LineDownOrNextHistory(n) => {
106            if !s.edit_move_line_down(n)? {
107                s.edit_history_next(false)?;
108            }
109        }
110        Cmd::HistorySearchBackward => s.edit_history_search(SearchDirection::Reverse)?,
111        Cmd::HistorySearchForward => s.edit_history_search(SearchDirection::Forward)?,
112        Cmd::TransposeChars => {
113            // Exchange the char before cursor with the character at cursor.
114            s.edit_transpose_chars()?;
115        }
116        Cmd::Yank(n, anchor) => {
117            // retrieve (yank) last item killed
118            let mut kill_ring = kill_ring.lock().unwrap();
119            if let Some(text) = kill_ring.yank() {
120                s.edit_yank(input_state, text, anchor, n)?;
121            }
122        }
123        Cmd::ViYankTo(ref mvt) => {
124            if let Some(text) = s.line.copy(mvt) {
125                let mut kill_ring = kill_ring.lock().unwrap();
126                kill_ring.kill(&text, Mode::Append);
127            }
128        }
129        Cmd::Newline => {
130            s.edit_insert('\n', 1)?;
131        }
132        Cmd::AcceptLine | Cmd::AcceptOrInsertLine { .. } => {
133            let validation_result = s.validate()?;
134            let valid = validation_result.is_valid();
135            let end = s.line.is_end_of_input();
136            match (cmd, valid, end) {
137                (Cmd::AcceptLine, ..)
138                | (Cmd::AcceptOrInsertLine { .. }, true, true)
139                | (
140                    Cmd::AcceptOrInsertLine {
141                        accept_in_the_middle: true,
142                    },
143                    true,
144                    _,
145                ) => {
146                    return Ok(Submit);
147                }
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.move_cursor_to_end()?;
230            return Err(error::ReadlineError::Interrupted);
231        }
232        _ => {
233            // Ignore the character typed.
234        }
235    }
236    Ok(Proceed)
237}