reedline/edit_mode/vi/
motion.rs1use std::iter::Peekable;
2
3use crate::{EditCommand, ReedlineEvent, Vi};
4
5use super::parser::{ParseResult, ReedlineOption};
6
7pub fn parse_motion<'iter, I>(
8 input: &mut Peekable<I>,
9 command_char: Option<char>,
10) -> ParseResult<Motion>
11where
12 I: Iterator<Item = &'iter char>,
13{
14 match input.peek() {
15 Some('h') => {
16 let _ = input.next();
17 ParseResult::Valid(Motion::Left)
18 }
19 Some('l') => {
20 let _ = input.next();
21 ParseResult::Valid(Motion::Right)
22 }
23 Some('j') => {
24 let _ = input.next();
25 ParseResult::Valid(Motion::Down)
26 }
27 Some('k') => {
28 let _ = input.next();
29 ParseResult::Valid(Motion::Up)
30 }
31 Some('b') => {
32 let _ = input.next();
33 ParseResult::Valid(Motion::PreviousWord)
34 }
35 Some('B') => {
36 let _ = input.next();
37 ParseResult::Valid(Motion::PreviousBigWord)
38 }
39 Some('w') => {
40 let _ = input.next();
41 ParseResult::Valid(Motion::NextWord)
42 }
43 Some('W') => {
44 let _ = input.next();
45 ParseResult::Valid(Motion::NextBigWord)
46 }
47 Some('e') => {
48 let _ = input.next();
49 ParseResult::Valid(Motion::NextWordEnd)
50 }
51 Some('E') => {
52 let _ = input.next();
53 ParseResult::Valid(Motion::NextBigWordEnd)
54 }
55 Some('0' | '^') => {
56 let _ = input.next();
57 ParseResult::Valid(Motion::Start)
58 }
59 Some('$') => {
60 let _ = input.next();
61 ParseResult::Valid(Motion::End)
62 }
63 Some('f') => {
64 let _ = input.next();
65 match input.peek() {
66 Some(&x) => {
67 input.next();
68 ParseResult::Valid(Motion::RightUntil(*x))
69 }
70 None => ParseResult::Incomplete,
71 }
72 }
73 Some('t') => {
74 let _ = input.next();
75 match input.peek() {
76 Some(&x) => {
77 input.next();
78 ParseResult::Valid(Motion::RightBefore(*x))
79 }
80 None => ParseResult::Incomplete,
81 }
82 }
83 Some('F') => {
84 let _ = input.next();
85 match input.peek() {
86 Some(&x) => {
87 input.next();
88 ParseResult::Valid(Motion::LeftUntil(*x))
89 }
90 None => ParseResult::Incomplete,
91 }
92 }
93 Some('T') => {
94 let _ = input.next();
95 match input.peek() {
96 Some(&x) => {
97 input.next();
98 ParseResult::Valid(Motion::LeftBefore(*x))
99 }
100 None => ParseResult::Incomplete,
101 }
102 }
103 Some(';') => {
104 let _ = input.next();
105 ParseResult::Valid(Motion::ReplayCharSearch)
106 }
107 Some(',') => {
108 let _ = input.next();
109 ParseResult::Valid(Motion::ReverseCharSearch)
110 }
111 ch if ch == command_char.as_ref().as_ref() && command_char.is_some() => {
112 let _ = input.next();
113 ParseResult::Valid(Motion::Line)
114 }
115 None => ParseResult::Incomplete,
116 _ => ParseResult::Invalid,
117 }
118}
119
120#[derive(Debug, PartialEq, Eq)]
121pub enum Motion {
122 Left,
123 Right,
124 Up,
125 Down,
126 NextWord,
127 NextBigWord,
128 NextWordEnd,
129 NextBigWordEnd,
130 PreviousWord,
131 PreviousBigWord,
132 Line,
133 Start,
134 End,
135 RightUntil(char),
136 RightBefore(char),
137 LeftUntil(char),
138 LeftBefore(char),
139 ReplayCharSearch,
140 ReverseCharSearch,
141}
142
143impl Motion {
144 pub fn to_reedline(&self, vi_state: &mut Vi) -> Vec<ReedlineOption> {
145 match self {
146 Motion::Left => vec![ReedlineOption::Event(ReedlineEvent::UntilFound(vec![
147 ReedlineEvent::MenuLeft,
148 ReedlineEvent::Left,
149 ]))],
150 Motion::Right => vec![ReedlineOption::Event(ReedlineEvent::UntilFound(vec![
151 ReedlineEvent::HistoryHintComplete,
152 ReedlineEvent::MenuRight,
153 ReedlineEvent::Right,
154 ]))],
155 Motion::Up => vec![ReedlineOption::Event(ReedlineEvent::UntilFound(vec![
156 ReedlineEvent::MenuUp,
157 ReedlineEvent::Up,
158 ]))],
159 Motion::Down => vec![ReedlineOption::Event(ReedlineEvent::UntilFound(vec![
160 ReedlineEvent::MenuDown,
161 ReedlineEvent::Down,
162 ]))],
163 Motion::NextWord => vec![ReedlineOption::Edit(EditCommand::MoveWordRightStart)],
164 Motion::NextBigWord => vec![ReedlineOption::Edit(EditCommand::MoveBigWordRightStart)],
165 Motion::NextWordEnd => vec![ReedlineOption::Edit(EditCommand::MoveWordRightEnd)],
166 Motion::NextBigWordEnd => vec![ReedlineOption::Edit(EditCommand::MoveBigWordRightEnd)],
167 Motion::PreviousWord => vec![ReedlineOption::Edit(EditCommand::MoveWordLeft)],
168 Motion::PreviousBigWord => vec![ReedlineOption::Edit(EditCommand::MoveBigWordLeft)],
169 Motion::Line => vec![], Motion::Start => vec![ReedlineOption::Edit(EditCommand::MoveToLineStart)],
171 Motion::End => vec![ReedlineOption::Edit(EditCommand::MoveToLineEnd)],
172 Motion::RightUntil(ch) => {
173 vi_state.last_char_search = Some(ViCharSearch::ToRight(*ch));
174 vec![ReedlineOption::Edit(EditCommand::MoveRightUntil(*ch))]
175 }
176 Motion::RightBefore(ch) => {
177 vi_state.last_char_search = Some(ViCharSearch::TillRight(*ch));
178 vec![ReedlineOption::Edit(EditCommand::MoveRightBefore(*ch))]
179 }
180 Motion::LeftUntil(ch) => {
181 vi_state.last_char_search = Some(ViCharSearch::ToLeft(*ch));
182 vec![ReedlineOption::Edit(EditCommand::MoveLeftUntil(*ch))]
183 }
184 Motion::LeftBefore(ch) => {
185 vi_state.last_char_search = Some(ViCharSearch::TillLeft(*ch));
186 vec![ReedlineOption::Edit(EditCommand::MoveLeftBefore(*ch))]
187 }
188 Motion::ReplayCharSearch => {
189 if let Some(char_search) = vi_state.last_char_search.as_ref() {
190 vec![ReedlineOption::Edit(char_search.to_move())]
191 } else {
192 vec![]
193 }
194 }
195 Motion::ReverseCharSearch => {
196 if let Some(char_search) = vi_state.last_char_search.as_ref() {
197 vec![ReedlineOption::Edit(char_search.reverse().to_move())]
198 } else {
199 vec![]
200 }
201 }
202 }
203 }
204}
205
206#[derive(Debug, PartialEq, Eq, Clone)]
208pub enum ViCharSearch {
209 ToRight(char),
211 ToLeft(char),
213 TillRight(char),
215 TillLeft(char),
217}
218
219impl ViCharSearch {
220 pub fn reverse(&self) -> Self {
222 match self {
223 ViCharSearch::ToRight(c) => ViCharSearch::ToLeft(*c),
224 ViCharSearch::ToLeft(c) => ViCharSearch::ToRight(*c),
225 ViCharSearch::TillRight(c) => ViCharSearch::TillLeft(*c),
226 ViCharSearch::TillLeft(c) => ViCharSearch::TillRight(*c),
227 }
228 }
229
230 pub fn to_move(&self) -> EditCommand {
231 match self {
232 ViCharSearch::ToRight(c) => EditCommand::MoveRightUntil(*c),
233 ViCharSearch::ToLeft(c) => EditCommand::MoveLeftUntil(*c),
234 ViCharSearch::TillRight(c) => EditCommand::MoveRightBefore(*c),
235 ViCharSearch::TillLeft(c) => EditCommand::MoveLeftBefore(*c),
236 }
237 }
238
239 pub fn to_cut(&self) -> EditCommand {
240 match self {
241 ViCharSearch::ToRight(c) => EditCommand::CutRightUntil(*c),
242 ViCharSearch::ToLeft(c) => EditCommand::CutLeftUntil(*c),
243 ViCharSearch::TillRight(c) => EditCommand::CutRightBefore(*c),
244 ViCharSearch::TillLeft(c) => EditCommand::CutLeftBefore(*c),
245 }
246 }
247}