rustyline/
command.rs

1use crate::complete_hint_line;
2use crate::config::Config;
3use crate::edit::State;
4use crate::error;
5use crate::history::SearchDirection;
6use crate::keymap::{Anchor, At, Cmd, Movement, Word};
7use crate::keymap::{InputState, Refresher};
8use crate::kill_ring::{KillRing, Mode};
9use crate::line_buffer::WordAction;
10use crate::{Helper, Result};
11
12pub enum Status {
13    Proceed,
14    Submit,
15}
16
17pub fn execute<H: Helper>(
18    cmd: Cmd,
19    s: &mut State<'_, '_, H>,
20    input_state: &InputState,
21    kill_ring: &mut KillRing,
22    config: &Config,
23) -> Result<Status> {
24    use Status::{Proceed, Submit};
25
26    match cmd {
27        Cmd::EndOfFile | Cmd::AcceptLine | Cmd::AcceptOrInsertLine { .. } | Cmd::Newline => {
28            if s.has_hint() || !s.is_default_prompt() || s.highlight_char {
29                // Force a refresh without hints to leave the previous
30                // line as the user typed it after a newline.
31                s.forced_refresh = true;
32                s.refresh_line_with_msg(None)?;
33                s.forced_refresh = false;
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, kill_ring)?;
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            if let Some(text) = kill_ring.yank() {
119                s.edit_yank(input_state, text, anchor, n)?;
120            }
121        }
122        Cmd::ViYankTo(ref mvt) => {
123            if let Some(text) = s.line.copy(mvt) {
124                kill_ring.kill(&text, Mode::Append);
125            }
126        }
127        Cmd::Newline => {
128            s.edit_insert('\n', 1)?;
129        }
130        Cmd::Repaint => {
131            s.refresh_line()?;
132        }
133        Cmd::AcceptLine | Cmd::AcceptOrInsertLine { .. } => {
134            let validation_result = s.validate()?;
135            let valid = validation_result.is_valid();
136            let end = s.line.is_end_of_input();
137            match (cmd, valid, end) {
138                (Cmd::AcceptLine, ..)
139                | (Cmd::AcceptOrInsertLine { .. }, true, true)
140                | (
141                    Cmd::AcceptOrInsertLine {
142                        accept_in_the_middle: true,
143                    },
144                    true,
145                    _,
146                ) => {
147                    return Ok(Submit);
148                }
149                (Cmd::AcceptOrInsertLine { .. }, false, _)
150                | (Cmd::AcceptOrInsertLine { .. }, true, false) => {
151                    if valid || !validation_result.has_message() {
152                        s.edit_insert('\n', 1)?;
153                    }
154                }
155                _ => unreachable!(),
156            }
157        }
158        Cmd::BeginningOfHistory => {
159            // move to first entry in history
160            s.edit_history(true)?;
161        }
162        Cmd::EndOfHistory => {
163            // move to last entry in history
164            s.edit_history(false)?;
165        }
166        Cmd::Move(Movement::BackwardWord(n, word_def)) => {
167            // move backwards one word
168            s.edit_move_to_prev_word(word_def, n)?;
169        }
170        Cmd::CapitalizeWord => {
171            // capitalize word after point
172            s.edit_word(WordAction::Capitalize)?;
173        }
174        Cmd::Kill(ref mvt) => {
175            s.edit_kill(mvt, kill_ring)?;
176        }
177        Cmd::Move(Movement::ForwardWord(n, at, word_def)) => {
178            // move forwards one word
179            s.edit_move_to_next_word(at, word_def, n)?;
180        }
181        Cmd::Move(Movement::LineUp(n)) => {
182            s.edit_move_line_up(n)?;
183        }
184        Cmd::Move(Movement::LineDown(n)) => {
185            s.edit_move_line_down(n)?;
186        }
187        Cmd::Move(Movement::BeginningOfBuffer) => {
188            // Move to the start of the buffer.
189            s.edit_move_buffer_start()?;
190        }
191        Cmd::Move(Movement::EndOfBuffer) => {
192            // Move to the end of the buffer.
193            s.edit_move_buffer_end()?;
194        }
195        Cmd::DowncaseWord => {
196            // lowercase word after point
197            s.edit_word(WordAction::Lowercase)?;
198        }
199        Cmd::TransposeWords(n) => {
200            // transpose words
201            s.edit_transpose_words(n)?;
202        }
203        Cmd::UpcaseWord => {
204            // uppercase word after point
205            s.edit_word(WordAction::Uppercase)?;
206        }
207        Cmd::YankPop => {
208            // yank-pop
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.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}