reedline/edit_mode/vi/
command.rs1use super::{motion::Motion, motion::ViCharSearch, parser::ReedlineOption};
2use crate::{EditCommand, ReedlineEvent, Vi};
3use std::iter::Peekable;
4
5pub fn parse_command<'iter, I>(input: &mut Peekable<I>) -> Option<Command>
6where
7 I: Iterator<Item = &'iter char>,
8{
9 match input.peek() {
10 Some('d') => {
11 let _ = input.next();
12 Some(Command::Delete)
13 }
14 Some('p') => {
15 let _ = input.next();
16 Some(Command::PasteAfter)
17 }
18 Some('P') => {
19 let _ = input.next();
20 Some(Command::PasteBefore)
21 }
22 Some('i') => {
23 let _ = input.next();
24 Some(Command::EnterViInsert)
25 }
26 Some('a') => {
27 let _ = input.next();
28 Some(Command::EnterViAppend)
29 }
30 Some('u') => {
31 let _ = input.next();
32 Some(Command::Undo)
33 }
34 Some('c') => {
35 let _ = input.next();
36 Some(Command::Change)
37 }
38 Some('x') => {
39 let _ = input.next();
40 Some(Command::DeleteChar)
41 }
42 Some('r') => {
43 let _ = input.next();
44 match input.next() {
45 Some(c) => Some(Command::ReplaceChar(*c)),
46 None => Some(Command::Incomplete),
47 }
48 }
49 Some('s') => {
50 let _ = input.next();
51 Some(Command::SubstituteCharWithInsert)
52 }
53 Some('?') => {
54 let _ = input.next();
55 Some(Command::HistorySearch)
56 }
57 Some('C') => {
58 let _ = input.next();
59 Some(Command::ChangeToLineEnd)
60 }
61 Some('D') => {
62 let _ = input.next();
63 Some(Command::DeleteToEnd)
64 }
65 Some('I') => {
66 let _ = input.next();
67 Some(Command::PrependToStart)
68 }
69 Some('A') => {
70 let _ = input.next();
71 Some(Command::AppendToEnd)
72 }
73 Some('S') => {
74 let _ = input.next();
75 Some(Command::RewriteCurrentLine)
76 }
77 Some('~') => {
78 let _ = input.next();
79 Some(Command::Switchcase)
80 }
81 Some('.') => {
82 let _ = input.next();
83 Some(Command::RepeatLastAction)
84 }
85 _ => None,
86 }
87}
88
89#[derive(Debug, PartialEq, Eq)]
90pub enum Command {
91 Incomplete,
92 Delete,
93 DeleteChar,
94 ReplaceChar(char),
95 SubstituteCharWithInsert,
96 PasteAfter,
97 PasteBefore,
98 EnterViAppend,
99 EnterViInsert,
100 Undo,
101 ChangeToLineEnd,
102 DeleteToEnd,
103 AppendToEnd,
104 PrependToStart,
105 RewriteCurrentLine,
106 Change,
107 HistorySearch,
108 Switchcase,
109 RepeatLastAction,
110}
111
112impl Command {
113 pub fn whole_line_char(&self) -> Option<char> {
114 match self {
115 Command::Delete => Some('d'),
116 Command::Change => Some('c'),
117 _ => None,
118 }
119 }
120
121 pub fn requires_motion(&self) -> bool {
122 matches!(self, Command::Delete | Command::Change)
123 }
124
125 pub fn to_reedline(&self, vi_state: &mut Vi) -> Vec<ReedlineOption> {
126 match self {
127 Self::EnterViInsert => vec![ReedlineOption::Event(ReedlineEvent::Repaint)],
128 Self::EnterViAppend => vec![ReedlineOption::Edit(EditCommand::MoveRight)],
129 Self::PasteAfter => vec![ReedlineOption::Edit(EditCommand::PasteCutBufferAfter)],
130 Self::PasteBefore => vec![ReedlineOption::Edit(EditCommand::PasteCutBufferBefore)],
131 Self::Undo => vec![ReedlineOption::Edit(EditCommand::Undo)],
132 Self::ChangeToLineEnd => vec![ReedlineOption::Edit(EditCommand::ClearToLineEnd)],
133 Self::DeleteToEnd => vec![ReedlineOption::Edit(EditCommand::CutToLineEnd)],
134 Self::AppendToEnd => vec![ReedlineOption::Edit(EditCommand::MoveToLineEnd)],
135 Self::PrependToStart => vec![ReedlineOption::Edit(EditCommand::MoveToLineStart)],
136 Self::RewriteCurrentLine => vec![ReedlineOption::Edit(EditCommand::CutCurrentLine)],
137 Self::DeleteChar => vec![ReedlineOption::Edit(EditCommand::CutChar)],
138 Self::ReplaceChar(c) => {
139 vec![ReedlineOption::Edit(EditCommand::ReplaceChar(*c))]
140 }
141 Self::SubstituteCharWithInsert => vec![ReedlineOption::Edit(EditCommand::CutChar)],
142 Self::HistorySearch => vec![ReedlineOption::Event(ReedlineEvent::SearchHistory)],
143 Self::Switchcase => vec![ReedlineOption::Edit(EditCommand::SwitchcaseChar)],
144 Self::Delete | Self::Change | Self::Incomplete => vec![ReedlineOption::Incomplete],
146 Command::RepeatLastAction => match &vi_state.previous {
147 Some(event) => vec![ReedlineOption::Event(event.clone())],
148 None => vec![],
149 },
150 }
151 }
152
153 pub fn to_reedline_with_motion(
154 &self,
155 motion: &Motion,
156 vi_state: &mut Vi,
157 ) -> Option<Vec<ReedlineOption>> {
158 match self {
159 Self::Delete => match motion {
160 Motion::End => Some(vec![ReedlineOption::Edit(EditCommand::CutToLineEnd)]),
161 Motion::Line => Some(vec![ReedlineOption::Edit(EditCommand::CutCurrentLine)]),
162 Motion::NextWord => {
163 Some(vec![ReedlineOption::Edit(EditCommand::CutWordRightToNext)])
164 }
165 Motion::NextBigWord => Some(vec![ReedlineOption::Edit(
166 EditCommand::CutBigWordRightToNext,
167 )]),
168 Motion::NextWordEnd => Some(vec![ReedlineOption::Edit(EditCommand::CutWordRight)]),
169 Motion::NextBigWordEnd => {
170 Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordRight)])
171 }
172 Motion::PreviousWord => Some(vec![ReedlineOption::Edit(EditCommand::CutWordLeft)]),
173 Motion::PreviousBigWord => {
174 Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordLeft)])
175 }
176 Motion::RightUntil(c) => {
177 vi_state.last_char_search = Some(ViCharSearch::ToRight(*c));
178 Some(vec![ReedlineOption::Edit(EditCommand::CutRightUntil(*c))])
179 }
180 Motion::RightBefore(c) => {
181 vi_state.last_char_search = Some(ViCharSearch::TillRight(*c));
182 Some(vec![ReedlineOption::Edit(EditCommand::CutRightBefore(*c))])
183 }
184 Motion::LeftUntil(c) => {
185 vi_state.last_char_search = Some(ViCharSearch::ToLeft(*c));
186 Some(vec![ReedlineOption::Edit(EditCommand::CutLeftUntil(*c))])
187 }
188 Motion::LeftBefore(c) => {
189 vi_state.last_char_search = Some(ViCharSearch::TillLeft(*c));
190 Some(vec![ReedlineOption::Edit(EditCommand::CutLeftBefore(*c))])
191 }
192 Motion::Start => Some(vec![ReedlineOption::Edit(EditCommand::CutFromLineStart)]),
193 Motion::Left => Some(vec![ReedlineOption::Edit(EditCommand::Backspace)]),
194 Motion::Right => Some(vec![ReedlineOption::Edit(EditCommand::Delete)]),
195 Motion::Up => None,
196 Motion::Down => None,
197 Motion::ReplayCharSearch => vi_state
198 .last_char_search
199 .as_ref()
200 .map(|char_search| vec![ReedlineOption::Edit(char_search.to_cut())]),
201 Motion::ReverseCharSearch => vi_state
202 .last_char_search
203 .as_ref()
204 .map(|char_search| vec![ReedlineOption::Edit(char_search.reverse().to_cut())]),
205 },
206 Self::Change => {
207 let op = match motion {
208 Motion::End => Some(vec![ReedlineOption::Edit(EditCommand::ClearToLineEnd)]),
209 Motion::Line => Some(vec![
210 ReedlineOption::Edit(EditCommand::MoveToStart),
211 ReedlineOption::Edit(EditCommand::ClearToLineEnd),
212 ]),
213 Motion::NextWord => Some(vec![ReedlineOption::Edit(EditCommand::CutWordRight)]),
214 Motion::NextBigWord => {
215 Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordRight)])
216 }
217 Motion::NextWordEnd => {
218 Some(vec![ReedlineOption::Edit(EditCommand::CutWordRight)])
219 }
220 Motion::NextBigWordEnd => {
221 Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordRight)])
222 }
223 Motion::PreviousWord => {
224 Some(vec![ReedlineOption::Edit(EditCommand::CutWordLeft)])
225 }
226 Motion::PreviousBigWord => {
227 Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordLeft)])
228 }
229 Motion::RightUntil(c) => {
230 vi_state.last_char_search = Some(ViCharSearch::ToRight(*c));
231 Some(vec![ReedlineOption::Edit(EditCommand::CutRightUntil(*c))])
232 }
233 Motion::RightBefore(c) => {
234 vi_state.last_char_search = Some(ViCharSearch::TillRight(*c));
235 Some(vec![ReedlineOption::Edit(EditCommand::CutRightBefore(*c))])
236 }
237 Motion::LeftUntil(c) => {
238 vi_state.last_char_search = Some(ViCharSearch::ToLeft(*c));
239 Some(vec![ReedlineOption::Edit(EditCommand::CutLeftUntil(*c))])
240 }
241 Motion::LeftBefore(c) => {
242 vi_state.last_char_search = Some(ViCharSearch::TillLeft(*c));
243 Some(vec![ReedlineOption::Edit(EditCommand::CutLeftBefore(*c))])
244 }
245 Motion::Start => {
246 Some(vec![ReedlineOption::Edit(EditCommand::CutFromLineStart)])
247 }
248 Motion::Left => Some(vec![ReedlineOption::Edit(EditCommand::Backspace)]),
249 Motion::Right => Some(vec![ReedlineOption::Edit(EditCommand::Delete)]),
250 Motion::Up => None,
251 Motion::Down => None,
252 Motion::ReplayCharSearch => vi_state
253 .last_char_search
254 .as_ref()
255 .map(|char_search| vec![ReedlineOption::Edit(char_search.to_cut())]),
256 Motion::ReverseCharSearch => {
257 vi_state.last_char_search.as_ref().map(|char_search| {
258 vec![ReedlineOption::Edit(char_search.reverse().to_cut())]
259 })
260 }
261 };
262 op.map(|mut vec| {
264 vec.push(ReedlineOption::Event(ReedlineEvent::Repaint));
265 vec
266 })
267 }
268 _ => None,
269 }
270 }
271}