1use bitflags::bitflags;
15
16use crate::{
17 actions::{
18 Action,
19 CommandAction,
20 CommandBarAction,
21 EditAction,
22 EditorAction,
23 HistoryAction,
24 InsertTextAction,
25 PromptAction,
26 SelectionAction,
27 WindowAction,
28 },
29 editing::application::{ApplicationInfo, EmptyInfo},
30 prelude::*,
31};
32
33use super::{
34 super::{keyparse::parse, CommonKeyClass, ShellBindings},
35 EmacsMode,
36 EmacsState,
37};
38
39use crate::key::TerminalKey;
40use crate::keybindings::{InputBindings, ModalMachine, Step};
41
42bitflags! {
43 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
44 struct MappedModes: u32 {
45 const I = 0b0000000000000001;
46 const C = 0b0000000000000010;
47 const S = 0b0000000000000100;
48
49 const IC = MappedModes::I.bits() | MappedModes::C.bits();
50 const ICS = MappedModes::IC.bits() | MappedModes::S.bits();
51 }
52}
53
54const MAP: MappedModes = MappedModes::ICS;
55const ICMAP: MappedModes = MappedModes::IC;
56const IMAP: MappedModes = MappedModes::I;
57const CMAP: MappedModes = MappedModes::C;
58const SMAP: MappedModes = MappedModes::S;
59
60impl MappedModes {
61 pub fn split(&self) -> Vec<EmacsMode> {
62 let mut modes = Vec::new();
63
64 if self.contains(MappedModes::I) {
65 modes.push(EmacsMode::Insert);
66 }
67
68 if self.contains(MappedModes::C) {
69 modes.push(EmacsMode::Command);
70 }
71
72 if self.contains(MappedModes::S) {
73 modes.push(EmacsMode::Search);
74 }
75
76 return modes;
77 }
78}
79
80#[derive(Clone, Debug)]
81enum InternalAction {
82 ClearTargetShape(bool),
83 SaveCounting(Option<usize>),
84 SetInsertStyle(InsertStyle),
85 SetRegister(Register),
86 SetSearchRegexParams(MoveDir1D, bool),
87 SetTargetShape(TargetShape, bool),
88}
89
90impl InternalAction {
91 pub fn run<I: ApplicationInfo>(&self, ctx: &mut EmacsState<I>) {
92 match self {
93 InternalAction::ClearTargetShape(shiftreq) => {
94 if *shiftreq {
95 if ctx.persist.shift {
96 ctx.persist.shape = None;
97 ctx.persist.shift = false;
98 }
99 } else {
100 ctx.persist.shape = None;
101 }
102 },
103 InternalAction::SaveCounting(optn) => {
104 let counting = match (optn, ctx.action.counting) {
105 (Some(n1), Some(n2)) => n1.saturating_mul(10).saturating_add(n2),
106 (Some(n), None) => *n,
107 (None, Some(n)) => n,
108 (None, None) => 4,
109 };
110
111 ctx.action.count = Some(match ctx.action.count {
112 None => counting,
113 Some(prev) => prev.saturating_mul(counting),
114 });
115
116 ctx.action.counting = None;
117 },
118 InternalAction::SetInsertStyle(style) => {
119 if style == &ctx.persist.insert {
120 ctx.persist.insert = !*style;
121 } else {
122 ctx.persist.insert = *style;
123 }
124 },
125 InternalAction::SetRegister(reg) => {
126 ctx.action.register = Some(reg.clone());
127 },
128 InternalAction::SetSearchRegexParams(dir, incremental) => {
129 ctx.persist.regexsearch_dir = *dir;
130 ctx.persist.regexsearch_inc = *incremental;
131 },
132 InternalAction::SetTargetShape(shape, shifted) => {
133 ctx.persist.shape = Some(*shape);
134 ctx.persist.shift = *shifted;
135 },
136 }
137 }
138}
139
140#[derive(Debug)]
141enum ExternalAction<I: ApplicationInfo> {
142 Something(Action<I>),
143 Repeat(bool),
144}
145
146impl<I: ApplicationInfo> ExternalAction<I> {
147 fn resolve(&self, ctx: &mut EmacsState<I>) -> Vec<Action<I>> {
148 match self {
149 ExternalAction::Something(act) => {
150 ctx.persist.repeating = false;
151
152 vec![act.clone()]
153 },
154 ExternalAction::Repeat(reqrep) => {
155 if *reqrep && !ctx.persist.repeating {
156 let ch = Char::Single('z').into();
157 let it = InsertTextAction::Type(ch, MoveDir1D::Previous, Count::Contextual);
158
159 return vec![it.into()];
160 }
161
162 ctx.persist.repeating = true;
163
164 return vec![Action::Repeat(RepeatType::LastAction)];
165 },
166 }
167 }
168}
169
170impl<I: ApplicationInfo> Clone for ExternalAction<I> {
171 fn clone(&self) -> Self {
172 match self {
173 ExternalAction::Something(act) => ExternalAction::Something(act.clone()),
174 ExternalAction::Repeat(reqrep) => ExternalAction::Repeat(*reqrep),
175 }
176 }
177}
178
179impl<I: ApplicationInfo> From<Action<I>> for ExternalAction<I> {
180 fn from(act: Action<I>) -> Self {
181 ExternalAction::Something(act)
182 }
183}
184
185#[derive(Debug, Default)]
187pub struct InputStep<I: ApplicationInfo> {
188 internal: Vec<InternalAction>,
189 external: Vec<ExternalAction<I>>,
190 nextm: Option<EmacsMode>,
191}
192
193impl<I: ApplicationInfo> InputStep<I> {
194 pub fn new() -> Self {
196 InputStep { internal: vec![], external: vec![], nextm: None }
197 }
198
199 pub fn goto(mut self, mode: EmacsMode) -> Self {
201 self.nextm = Some(mode);
202 self
203 }
204
205 pub fn actions(mut self, acts: Vec<Action<I>>) -> Self {
207 self.external = acts.into_iter().map(ExternalAction::Something).collect();
208 self
209 }
210}
211
212impl<I: ApplicationInfo> Clone for InputStep<I> {
213 fn clone(&self) -> Self {
214 Self {
215 internal: self.internal.clone(),
216 external: self.external.clone(),
217 nextm: self.nextm,
218 }
219 }
220}
221
222impl<I: ApplicationInfo> Step<TerminalKey> for InputStep<I> {
223 type A = Action<I>;
224 type State = EmacsState<I>;
225 type M = EmacsMode;
226 type Class = CommonKeyClass;
227 type Sequence = RepeatType;
228
229 fn is_unmapped(&self) -> bool {
230 match self {
231 InputStep { internal, external, nextm: None } => {
232 internal.is_empty() && external.is_empty()
233 },
234 _ => false,
235 }
236 }
237
238 fn fallthrough(&self) -> Option<Self::M> {
239 None
240 }
241
242 fn step(&self, ctx: &mut EmacsState<I>) -> (Vec<Action<I>>, Option<Self::M>) {
243 for iact in self.internal.iter() {
244 iact.run(ctx);
245 }
246
247 let external: Vec<Action<I>> =
248 self.external.iter().flat_map(|act| act.resolve(ctx)).collect();
249
250 return (external, self.nextm);
251 }
252}
253
254macro_rules! act {
255 ($ext: expr) => {
256 isv!(vec![], vec![ExternalAction::Something($ext)])
257 };
258 ($ext: expr, $ns: expr) => {
259 isv!(vec![], vec![ExternalAction::Something($ext)], $ns)
260 };
261}
262
263macro_rules! iact {
264 ($int: expr) => {
265 isv!(vec![$int], vec![])
266 };
267 ($int: expr, $ns: expr) => {
268 isv!(vec![$int], vec![], $ns)
269 };
270}
271
272macro_rules! isv {
273 () => {
274 InputStep { internal: vec![], external: vec![], nextm: None }
275 };
276 ($ints: expr, $exts: expr) => {
277 InputStep { internal: $ints, external: $exts, nextm: None }
278 };
279 ($ints: expr, $exts: expr, $ns: expr) => {
280 InputStep { internal: $ints, external: $exts, nextm: Some($ns) }
281 };
282}
283
284macro_rules! is {
285 ($int: expr, $ext: expr) => {
286 isv!(vec![$int], vec![ExternalAction::Something($ext.into())])
287 };
288 ($int: expr, $ext: expr, $ns: expr) => {
289 isv!(vec![$int], vec![ExternalAction::Something($ext.into())], $ns)
290 };
291}
292
293macro_rules! blackhole {
294 ($act: expr) => {
295 is!(InternalAction::SetRegister(Register::Blackhole), $act)
296 };
297 ($act: expr, $nm: expr) => {
298 is!(InternalAction::SetRegister(Register::Blackhole), $act, $nm)
299 };
300}
301
302macro_rules! motion {
303 ($mt: expr) => {
304 is!(
305 InternalAction::ClearTargetShape(true),
306 EditorAction::Edit(
307 Specifier::Exact(EditAction::Motion),
308 EditTarget::Motion($mt, Count::Contextual)
309 )
310 )
311 };
312 ($mt: expr, $c: literal) => {
313 is!(
314 InternalAction::ClearTargetShape(true),
315 EditorAction::Edit(
316 Specifier::Exact(EditAction::Motion),
317 EditTarget::Motion($mt, Count::Exact($c))
318 )
319 )
320 };
321 ($mt: expr, $c: expr) => {
322 is!(
323 InternalAction::ClearTargetShape(true),
324 EditorAction::Edit(Specifier::Exact(EditAction::Motion), EditTarget::Motion($mt, $c))
325 )
326 };
327}
328
329macro_rules! yank_target {
330 ($target: expr) => {
331 edit_target!(EditAction::Yank, $target)
332 };
333 ($target: expr, $c: expr) => {
334 edit_target!(EditAction::Yank, $target, $c)
335 };
336}
337
338macro_rules! kill_target {
339 ($target: expr) => {
340 edit_target!(EditAction::Delete, $target)
341 };
342 ($target: expr, $c: expr) => {
343 edit_target!(EditAction::Delete, $target, $c)
344 };
345}
346
347macro_rules! kill {
348 ($mt: expr) => {
349 edit!(EditAction::Delete, $mt)
350 };
351 ($mt: expr, $c: expr) => {
352 edit!(EditAction::Delete, $mt, $c)
353 };
354}
355
356macro_rules! just_one_space {
357 () => {
358 isv!(vec![InternalAction::SetRegister(Register::Blackhole)], vec![
359 Action::from(EditorAction::Edit(
360 EditAction::Delete.into(),
361 RangeType::Word(WordStyle::Whitespace(false)).into()
362 ))
363 .into(),
364 Action::from(InsertTextAction::Type(
365 Char::Single(' ').into(),
366 MoveDir1D::Previous,
367 Count::Contextual
368 ))
369 .into()
370 ])
371 };
372}
373
374macro_rules! cmdbar_focus {
375 ($type: expr, $nm: expr, $act: expr) => {
376 cmdbar!(CommandBarAction::Focus(":".into(), $type, Box::new(Action::from($act))), $nm)
377 };
378}
379
380macro_rules! cmdbar_search {
381 ($dir: expr) => {
382 is!(
383 InternalAction::SetSearchRegexParams($dir, true),
384 Action::CommandBar(CommandBarAction::Focus(
385 match $dir {
386 MoveDir1D::Next => "/".into(),
387 MoveDir1D::Previous => "?".into(),
388 },
389 CommandType::Search,
390 Box::new(
391 EditorAction::Edit(
392 Specifier::Contextual,
393 EditTarget::Search(SearchType::Regex, MoveDirMod::Same, Count::Contextual)
394 )
395 .into()
396 )
397 )),
398 EmacsMode::Search
399 )
400 };
401}
402
403macro_rules! window {
404 ($act: expr) => {
405 act!(Action::Window($act))
406 };
407}
408
409macro_rules! window_focus {
410 ($fc: expr) => {
411 window!(WindowAction::Focus($fc))
412 };
413}
414
415macro_rules! window_switch {
416 ($st: expr) => {
417 window!(WindowAction::Switch($st))
418 };
419}
420
421macro_rules! window_split {
422 ($axis: expr) => {
423 window!(WindowAction::Split(
424 OpenTarget::Current,
425 $axis,
426 MoveDir1D::Previous,
427 Count::Contextual
428 ))
429 };
430}
431
432macro_rules! window_close {
433 ($style: expr, $fc: expr, $flags: expr) => {
434 window!(WindowAction::Close($style($fc), $flags))
435 };
436}
437
438macro_rules! window_quit {
439 ($style: expr, $f: expr) => {
440 window_close!($style, $f, CloseFlags::QUIT)
441 };
442}
443
444macro_rules! search {
445 ($dir: expr) => {
446 is!(
447 InternalAction::SetSearchRegexParams($dir, true),
448 Action::Search(MoveDirMod::Exact($dir), Count::Exact(1))
449 )
450 };
451}
452
453macro_rules! search_word {
454 ($style: expr) => {
455 edit_target!(
456 EditAction::Motion,
457 EditTarget::Search(SearchType::Word($style, true), MoveDirMod::Same, Count::Exact(1))
458 )
459 };
460}
461
462macro_rules! start_selection {
463 ($shape: expr) => {
464 is!(
465 InternalAction::SetTargetShape($shape, false),
466 EditorAction::Selection(SelectionAction::Resize(
467 SelectionResizeStyle::Restart,
468 EditTarget::CurrentPosition
469 ))
470 )
471 };
472 ($shape: expr, $target: expr) => {
473 is!(
474 InternalAction::SetTargetShape($shape, false),
475 EditorAction::Selection(SelectionAction::Resize(
476 SelectionResizeStyle::Restart,
477 $target
478 ))
479 )
480 };
481}
482
483macro_rules! start_shift_selection {
484 ($target: expr) => {
485 is!(
486 InternalAction::SetTargetShape(TargetShape::CharWise, true),
487 EditorAction::Edit(Specifier::Exact(EditAction::Motion), $target)
488 )
489 };
490}
491
492#[rustfmt::skip]
493fn default_keys<I: ApplicationInfo>() -> Vec<(MappedModes, &'static str, InputStep<I>)> {
494 [
495 ( MAP, "<C-Y>", paste!(PasteStyle::Cursor) ),
497 ( MAP, "<M-BS>", kill!(MoveType::WordBegin(WordStyle::Little, MoveDir1D::Previous)) ),
498 ( MAP, "<M-Del>", kill!(MoveType::WordBegin(WordStyle::Little, MoveDir1D::Previous)) ),
499 ( MAP, "<BS>", erase!(MoveType::Column(MoveDir1D::Previous, true)) ),
500
501 ( ICMAP, "<C-A>", motion!(MoveType::LinePos(MovePosition::Beginning), Count::MinusOne) ),
503 ( ICMAP, "<C-B>", motion!(MoveType::Column(MoveDir1D::Previous, true)) ),
504 ( ICMAP, "<C-D>", erase!(MoveType::Column(MoveDir1D::Next, true)) ),
505 ( ICMAP, "<C-E>", motion!(MoveType::LinePos(MovePosition::End), Count::MinusOne) ),
506 ( ICMAP, "<C-F>", motion!(MoveType::Column(MoveDir1D::Next, true)) ),
507 ( ICMAP, "<C-K>", kill!(MoveType::LinePos(MovePosition::End), Count::MinusOne) ),
508 ( ICMAP, "<C-X>z", isv!(vec![], vec![ExternalAction::Repeat(false)]) ),
509 ( ICMAP, "<C-W>", kill_target!(EditTarget::Selection) ),
510 ( ICMAP, "<C-Left>", motion!(MoveType::WordBegin(WordStyle::Little, MoveDir1D::Previous)) ),
511 ( ICMAP, "<C-Right>", motion!(MoveType::WordBegin(WordStyle::NonAlphaNum, MoveDir1D::Next)) ),
512 ( ICMAP, "<C-S-Left>", start_shift_selection!(MoveType::WordBegin(WordStyle::Little, MoveDir1D::Previous).into()) ),
513 ( ICMAP, "<C-S-Right>", start_shift_selection!(MoveType::WordBegin(WordStyle::NonAlphaNum, MoveDir1D::Next).into()) ),
514 ( ICMAP, "<M-b>", motion!(MoveType::WordBegin(WordStyle::Little, MoveDir1D::Previous)) ),
515 ( ICMAP, "<M-B>", start_shift_selection!(MoveType::WordBegin(WordStyle::Little, MoveDir1D::Previous).into()) ),
516 ( ICMAP, "<M-c>", edit!(EditAction::ChangeCase(Case::Title), MoveType::WordEnd(WordStyle::Little, MoveDir1D::Next)) ),
517 ( ICMAP, "<M-d>", kill!(MoveType::WordEnd(WordStyle::Little, MoveDir1D::Next)) ),
518 ( ICMAP, "<M-f>", motion!(MoveType::WordBegin(WordStyle::NonAlphaNum, MoveDir1D::Next)) ),
519 ( ICMAP, "<M-F>", start_shift_selection!(MoveType::WordBegin(WordStyle::NonAlphaNum, MoveDir1D::Next).into()) ),
520 ( ICMAP, "<M-l>", edit!(EditAction::ChangeCase(Case::Lower), MoveType::WordEnd(WordStyle::Little, MoveDir1D::Next)) ),
521 ( ICMAP, "<M-u>", edit!(EditAction::ChangeCase(Case::Upper), MoveType::WordEnd(WordStyle::Little, MoveDir1D::Next)) ),
522 ( ICMAP, "<M-w>", yank_target!(EditTarget::Selection) ),
523 ( ICMAP, "<M-W>", yank_target!(EditTarget::Selection) ),
524 ( ICMAP, "<M-Left>", motion!(MoveType::WordBegin(WordStyle::Little, MoveDir1D::Previous)) ),
525 ( ICMAP, "<M-Right>", motion!(MoveType::WordBegin(WordStyle::NonAlphaNum, MoveDir1D::Next)) ),
526 ( ICMAP, "<M-S-Left>", start_shift_selection!(MoveType::WordBegin(WordStyle::Little, MoveDir1D::Previous).into()) ),
527 ( ICMAP, "<M-S-Right>", start_shift_selection!(MoveType::WordBegin(WordStyle::NonAlphaNum, MoveDir1D::Next).into()) ),
528 ( ICMAP, "<S-End>", start_shift_selection!(MoveType::LinePos(MovePosition::End).into()) ),
529 ( ICMAP, "<S-Home>", start_shift_selection!(MoveType::LinePos(MovePosition::Beginning).into()) ),
530 ( ICMAP, "<S-Left>", start_shift_selection!(MoveType::Column(MoveDir1D::Previous, true).into()) ),
531 ( ICMAP, "<S-Right>", start_shift_selection!(MoveType::Column(MoveDir1D::Next, true).into()) ),
532 ( ICMAP, "<Del>", erase!(MoveType::Column(MoveDir1D::Next, true)) ),
533 ( ICMAP, "<End>", motion!(MoveType::LinePos(MovePosition::End), Count::MinusOne) ),
534 ( ICMAP, "<Home>", motion!(MoveType::LinePos(MovePosition::Beginning), Count::MinusOne) ),
535 ( ICMAP, "<Left>", motion!(MoveType::Column(MoveDir1D::Previous, true)) ),
536 ( ICMAP, "<Right>", motion!(MoveType::Column(MoveDir1D::Next, true)) ),
537 ( ICMAP, "z", isv!(vec![], vec![ExternalAction::Repeat(true)]) ),
538
539 ( IMAP, "<C-G>", is!(InternalAction::ClearTargetShape(false), EditorAction::Edit(EditAction::Motion.into(), EditTarget::CurrentPosition), EmacsMode::Insert) ),
541 ( IMAP, "<C-J>", chartype!(Char::Single('\n')) ),
542 ( IMAP, "<C-L>", scrollcp!(MovePosition::Middle, Axis::Vertical) ),
543 ( IMAP, "<C-N>", motion!(MoveType::Line(MoveDir1D::Next)) ),
544 ( IMAP, "<C-O>", insert_text!(InsertTextAction::OpenLine(TargetShape::CharWise, MoveDir1D::Previous, Count::Contextual)) ),
545 ( IMAP, "<C-P>", motion!(MoveType::Line(MoveDir1D::Previous)) ),
546 ( IMAP, "<C-Q>{oct<=3}", chartype!() ),
547 ( IMAP, "<C-Q>{any}", chartype!() ),
548 ( IMAP, "<C-R>", cmdbar_search!(MoveDir1D::Previous) ),
549 ( IMAP, "<C-S>", cmdbar_search!(MoveDir1D::Next) ),
550 ( IMAP, "<C-T>", unmapped!() ),
551 ( IMAP, "<C-U><C-X>s", unmapped!() ),
552 ( IMAP, "<C-V>", scroll2d!(MoveDir2D::Down, ScrollSize::Page) ),
553 ( IMAP, "<C-X><C-C>", window!(WindowAction::Close(WindowTarget::All, CloseFlags::QUIT)) ),
554 ( IMAP, "<C-X><C-O>", unmapped!() ),
555 ( IMAP, "<C-X><C-Q>", unmapped!() ),
556 ( IMAP, "<C-X><C-S>", unmapped!() ),
557 ( IMAP, "<C-X><C-T>", unmapped!() ),
558 ( IMAP, "<C-X><C-W>", unmapped!() ),
559 ( IMAP, "<C-X><C-X>", selection!(SelectionAction::CursorSet(SelectionCursorChange::SwapAnchor)) ),
560 ( IMAP, "<C-X><C-Z>", act!(Action::Suspend) ),
561 ( IMAP, "<C-X><Space>", unmapped!() ),
562 ( IMAP, "<C-X><Left>", window_switch!(OpenTarget::Offset(MoveDir1D::Previous, Count::Contextual)) ),
563 ( IMAP, "<C-X><Right>", window_switch!(OpenTarget::Offset(MoveDir1D::Next, Count::Contextual)) ),
564 ( IMAP, "<C-X>b", unmapped!() ),
565 ( IMAP, "<C-X>h", start_selection!(TargetShape::CharWise, RangeType::Buffer.into()) ),
566 ( IMAP, "<C-X>k", unmapped!() ),
567 ( IMAP, "<C-X>o", window_focus!(FocusChange::Direction1D(MoveDir1D::Next, Count::Exact(1), true)) ),
568 ( IMAP, "<C-X>r<M-w>", unmapped!() ),
569 ( IMAP, "<C-X>rb", unmapped!() ),
570 ( IMAP, "<C-X>rc", unmapped!() ),
571 ( IMAP, "<C-X>rd", unmapped!() ),
572 ( IMAP, "<C-X>rk", unmapped!() ),
573 ( IMAP, "<C-X>rl", unmapped!() ),
574 ( IMAP, "<C-X>rm", unmapped!() ),
575 ( IMAP, "<C-X>ro", unmapped!() ),
576 ( IMAP, "<C-X>ry", unmapped!() ),
577 ( IMAP, "<C-X>rN", unmapped!() ),
578 ( IMAP, "<C-X>s", unmapped!() ),
579 ( IMAP, "<C-X>u", history!(HistoryAction::Undo(Count::Contextual)) ),
580 ( IMAP, "<C-X>0", window_quit!(WindowTarget::Single, FocusChange::Current) ),
581 ( IMAP, "<C-X>1", window_quit!(WindowTarget::AllBut, FocusChange::Current) ),
582 ( IMAP, "<C-X>2", window_split!(Axis::Horizontal) ),
583 ( IMAP, "<C-X>3", window_split!(Axis::Vertical) ),
584 ( IMAP, "<C-X><", scroll2d!(MoveDir2D::Left, ScrollSize::Page) ),
585 ( IMAP, "<C-X>>", scroll2d!(MoveDir2D::Right, ScrollSize::Page) ),
586 ( IMAP, "<C-Z>", act!(Action::Suspend) ),
587 ( IMAP, "<C-@>", start_selection!(TargetShape::CharWise) ),
588 ( IMAP, "<C-_>", history!(HistoryAction::Undo(Count::Contextual)) ),
589 ( IMAP, "<C-Del>", kill!(MoveType::LinePos(MovePosition::End), Count::MinusOne) ),
590 ( IMAP, "<C-Space>", start_selection!(TargetShape::CharWise) ),
591 ( IMAP, "<M-s>.", search_word!(WordStyle::Big) ),
592 ( IMAP, "<M-t>", unmapped!() ),
593 ( IMAP, "<M-v>", scroll2d!(MoveDir2D::Up, ScrollSize::Page) ),
594 ( IMAP, "<M-x>", cmdbar_focus!(CommandType::Command, EmacsMode::Command, CommandAction::Execute(1.into())) ),
595 ( IMAP, "<M-z>{char}", kill_target!(EditTarget::Search(SearchType::Char(true), MoveDirMod::Same, Count::Contextual)) ),
596 ( IMAP, "<M-S>.", search_word!(WordStyle::Big) ),
597 ( IMAP, "<M-\\>", erase_range!(RangeType::Word(WordStyle::Whitespace(false))) ),
598 ( IMAP, "<M-^>", edit!(EditAction::Join(JoinStyle::OneSpace), MoveType::Line(MoveDir1D::Previous)) ),
599 ( IMAP, "<M-<>", edit_buffer!(EditAction::Motion, MoveTerminus::Beginning) ),
600 ( IMAP, "<M->>", edit_buffer!(EditAction::Motion, MoveTerminus::End) ),
601 ( IMAP, "<M-Space>", just_one_space!() ),
602 ( IMAP, "<S-Up>", start_shift_selection!(MoveType::Line(MoveDir1D::Previous).into()) ),
603 ( IMAP, "<S-Down>", start_shift_selection!(MoveType::Line(MoveDir1D::Next).into()) ),
604 ( IMAP, "<Insert>", iact!(InternalAction::SetInsertStyle(InsertStyle::Replace)) ),
605 ( IMAP, "<Up>", motion!(MoveType::Line(MoveDir1D::Previous)) ),
606 ( IMAP, "<Down>", motion!(MoveType::Line(MoveDir1D::Next)) ),
607 ( IMAP, "<PageDown>", scroll2d!(MoveDir2D::Down, ScrollSize::Page) ),
608 ( IMAP, "<PageUp>", scroll2d!(MoveDir2D::Up, ScrollSize::Page) ),
609
610 ( CMAP, "<C-G>", prompt!(PromptAction::Abort(false), EmacsMode::Insert) ),
612 ( CMAP, "<C-I>", editor!(EditorAction::Complete(CompletionStyle::Single, CompletionType::Auto, CompletionDisplay::Bar)) ),
613 ( CMAP, "<Up>", prompt!(PromptAction::Recall(RecallFilter::All, MoveDir1D::Previous, Count::Contextual)) ),
614 ( CMAP, "<Down>", prompt!(PromptAction::Recall(RecallFilter::All, MoveDir1D::Next, Count::Contextual)) ),
615 ( CMAP, "<Space>", editor!(EditorAction::Complete(CompletionStyle::Prefix, CompletionType::Auto, CompletionDisplay::Bar)) ),
616 ( CMAP, "<Tab>", editor!(EditorAction::Complete(CompletionStyle::Single, CompletionType::Auto, CompletionDisplay::Bar)) ),
617
618 ( SMAP, "<C-G>", prompt!(PromptAction::Abort(false), EmacsMode::Insert) ),
620 ( SMAP, "<C-R>", search!(MoveDir1D::Previous) ),
621 ( SMAP, "<C-S>", search!(MoveDir1D::Next) ),
622 ].to_vec()
623}
624
625#[rustfmt::skip]
626fn default_pfxs<I: ApplicationInfo>() -> Vec<(MappedModes, &'static str, Option<InputStep<I>>)> {
627 [
628 ( ICMAP, "<C-U>{count*}", Some(iact!(InternalAction::SaveCounting(None))) ),
630 ( ICMAP, "<M-1>{count*}", Some(iact!(InternalAction::SaveCounting(1.into()))) ),
631 ( ICMAP, "<M-2>{count*}", Some(iact!(InternalAction::SaveCounting(2.into()))) ),
632 ( ICMAP, "<M-3>{count*}", Some(iact!(InternalAction::SaveCounting(3.into()))) ),
633 ( ICMAP, "<M-4>{count*}", Some(iact!(InternalAction::SaveCounting(4.into()))) ),
634 ( ICMAP, "<M-5>{count*}", Some(iact!(InternalAction::SaveCounting(5.into()))) ),
635 ( ICMAP, "<M-6>{count*}", Some(iact!(InternalAction::SaveCounting(6.into()))) ),
636 ( ICMAP, "<M-7>{count*}", Some(iact!(InternalAction::SaveCounting(7.into()))) ),
637 ( ICMAP, "<M-8>{count*}", Some(iact!(InternalAction::SaveCounting(8.into()))) ),
638 ( ICMAP, "<M-9>{count*}", Some(iact!(InternalAction::SaveCounting(9.into()))) ),
639 ( ICMAP, "<M-0>{count*}", Some(iact!(InternalAction::SaveCounting(0.into()))) ),
640 ].to_vec()
641}
642
643#[rustfmt::skip]
644fn default_enter<I: ApplicationInfo>() -> Vec<(MappedModes, &'static str, InputStep<I>)> {
645 [
646 ( IMAP, "<Enter>", chartype!(Char::Single('\n')) ),
648
649 ( CMAP, "<Enter>", prompt!(PromptAction::Submit, EmacsMode::Insert) ),
651 ].to_vec()
652}
653
654#[rustfmt::skip]
655fn submit_on_enter<I: ApplicationInfo>() -> Vec<(MappedModes, &'static str, InputStep<I>)> {
656 [
657 ( ICMAP, "<Enter>", prompt!(PromptAction::Submit, EmacsMode::Insert) ),
659 ].to_vec()
660}
661
662#[inline]
663fn add_prefix<I: ApplicationInfo>(
664 machine: &mut EmacsMachine<TerminalKey, I>,
665 modes: &MappedModes,
666 keys: &str,
667 action: &Option<InputStep<I>>,
668) {
669 if let Ok((_, evs)) = parse(keys) {
670 for mode in modes.split() {
671 machine.add_prefix(mode, &evs, action);
672 }
673 } else {
674 panic!("invalid Emacs keybinding: {}", keys);
675 }
676}
677
678#[inline]
679fn add_mapping<I: ApplicationInfo>(
680 machine: &mut EmacsMachine<TerminalKey, I>,
681 modes: &MappedModes,
682 keys: &str,
683 action: &InputStep<I>,
684) {
685 if let Ok((_, evs)) = parse(keys) {
686 for mode in modes.split() {
687 machine.add_mapping(mode, &evs, action);
688 }
689 } else {
690 panic!("invalid Emacs keybinding: {}", keys);
691 }
692}
693
694#[derive(Debug)]
696pub struct EmacsBindings<I: ApplicationInfo> {
697 prefixes: Vec<(MappedModes, &'static str, Option<InputStep<I>>)>,
698 mappings: Vec<(MappedModes, &'static str, InputStep<I>)>,
699 enter: Vec<(MappedModes, &'static str, InputStep<I>)>,
700}
701
702impl<I: ApplicationInfo> EmacsBindings<I> {
703 pub fn submit_on_enter(mut self) -> Self {
705 self.enter = submit_on_enter();
706 self
707 }
708}
709
710impl<I: ApplicationInfo> ShellBindings for EmacsBindings<I> {
711 fn shell(self) -> Self {
712 self.submit_on_enter()
713 }
714}
715
716impl<I: ApplicationInfo> Default for EmacsBindings<I> {
717 fn default() -> Self {
718 EmacsBindings {
719 prefixes: default_pfxs(),
720 mappings: default_keys(),
721 enter: default_enter(),
722 }
723 }
724}
725
726impl<I: ApplicationInfo> InputBindings<TerminalKey, InputStep<I>> for EmacsBindings<I> {
727 fn setup(&self, machine: &mut EmacsMachine<TerminalKey, I>) {
728 for (modes, keys, action) in self.prefixes.iter() {
729 add_prefix(machine, modes, keys, action);
730 }
731
732 for (modes, keys, action) in self.mappings.iter() {
733 add_mapping(machine, modes, keys, action);
734 }
735
736 for (modes, keys, action) in self.enter.iter() {
737 add_mapping(machine, modes, keys, action);
738 }
739 }
740}
741
742pub type EmacsMachine<Key, T = EmptyInfo> = ModalMachine<Key, InputStep<T>>;
744
745pub fn default_emacs_keys<I: ApplicationInfo>() -> EmacsMachine<TerminalKey, I> {
747 ModalMachine::from_bindings::<EmacsBindings<I>>()
748}
749
750#[cfg(test)]
751mod tests {
752 use super::*;
753 use crate::editing::context::EditContext;
754 use crate::keybindings::BindingMachine;
755 use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
756
757 macro_rules! mv {
758 ($mt: expr) => {
759 Action::from(EditorAction::Edit(
760 EditAction::Motion.into(),
761 EditTarget::Motion($mt, Count::Contextual),
762 ))
763 };
764 ($mt: expr, $c: literal) => {
765 Action::from(EditorAction::Edit(
766 EditAction::Motion.into(),
767 EditTarget::Motion($mt, Count::Exact($c)),
768 ))
769 };
770 ($mt: expr, $c: expr) => {
771 Action::from(EditorAction::Edit(EditAction::Motion.into(), EditTarget::Motion($mt, $c)))
772 };
773 }
774
775 macro_rules! typechar {
776 ($c: literal) => {
777 Action::from(EditorAction::InsertText(InsertTextAction::Type(
778 Char::Single($c).into(),
779 MoveDir1D::Previous,
780 Count::Contextual,
781 )))
782 };
783 }
784
785 const CMDBAR_ABORT: Action = Action::Prompt(PromptAction::Abort(false));
786
787 fn cmdbar_focus(s: &str, ct: CommandType, act: Action) -> Action {
788 Action::CommandBar(CommandBarAction::Focus(s.into(), ct, act.into()))
789 }
790
791 fn cmdbar_search(s: &str) -> Action {
792 let search = EditTarget::Search(SearchType::Regex, MoveDirMod::Same, Count::Contextual);
793 let search = EditorAction::Edit(Specifier::Contextual, search);
794 cmdbar_focus(s, CommandType::Search, search.into())
795 }
796
797 fn mkctx() -> EmacsState {
798 EmacsState::default()
799 }
800
801 #[test]
802 fn test_selection_shift() {
803 let mut vm: EmacsMachine<TerminalKey> = default_emacs_keys();
804 let mut ctx = mkctx();
805
806 assert_eq!(vm.mode(), EmacsMode::Insert);
808
809 let mov = mv!(MoveType::Column(MoveDir1D::Previous, true));
811 ctx.persist.shape = Some(TargetShape::CharWise);
812
813 vm.input_key(key!(KeyCode::Left, KeyModifiers::SHIFT));
814 assert_pop2!(vm, mov, ctx);
815 assert_eq!(vm.state().persist.shift, true);
816 assert_eq!(vm.mode(), EmacsMode::Insert);
817
818 let mov = mv!(MoveType::Column(MoveDir1D::Previous, true));
820 ctx.persist.shape = None;
821
822 vm.input_key(key!(KeyCode::Left));
823 assert_pop2!(vm, mov, ctx);
824 assert_eq!(vm.state().persist.shift, false);
825 assert_eq!(vm.mode(), EmacsMode::Insert);
826 }
827
828 #[test]
829 fn test_selection_no_shift() {
830 let mut vm: EmacsMachine<TerminalKey> = default_emacs_keys();
831 let mut ctx = mkctx();
832
833 ctx.persist.shape = Some(TargetShape::CharWise);
834
835 assert_eq!(vm.mode(), EmacsMode::Insert);
837
838 let act =
840 SelectionAction::Resize(SelectionResizeStyle::Restart, EditTarget::CurrentPosition);
841 vm.input_key(ctl!(' '));
842 assert_pop2!(vm, Action::from(act), ctx);
843
844 let mov = mv!(MoveType::Column(MoveDir1D::Previous, true));
846 vm.input_key(key!(KeyCode::Left));
847 assert_pop2!(vm, mov, ctx);
848 assert_eq!(vm.mode(), EmacsMode::Insert);
849 }
850
851 #[test]
852 fn test_search() {
853 let mut vm: EmacsMachine<TerminalKey> = default_emacs_keys();
854 let mut ctx = mkctx();
855
856 assert_eq!(vm.mode(), EmacsMode::Insert);
858
859 ctx.persist.regexsearch_dir = MoveDir1D::Previous;
861 vm.input_key(ctl!('r'));
862 assert_pop2!(vm, cmdbar_search("?"), ctx);
863 assert_eq!(vm.mode(), EmacsMode::Search);
864
865 let it =
867 InsertTextAction::Type(Char::from('r').into(), MoveDir1D::Previous, Count::Contextual);
868 vm.input_key(key!('r'));
869 assert_pop2!(vm, Action::from(it), ctx);
870 assert_eq!(vm.mode(), EmacsMode::Search);
871
872 let act = Action::Search(MoveDirMod::Exact(MoveDir1D::Previous), 1.into());
874 vm.input_key(ctl!('r'));
875 assert_pop2!(vm, act, ctx);
876 assert_eq!(vm.mode(), EmacsMode::Search);
877
878 ctx.persist.regexsearch_dir = MoveDir1D::Next;
880 let act = Action::Search(MoveDirMod::Exact(MoveDir1D::Next), 1.into());
881 vm.input_key(ctl!('s'));
882 assert_pop2!(vm, act, ctx);
883 assert_eq!(vm.mode(), EmacsMode::Search);
884
885 vm.input_key(key!(KeyCode::Left));
887 assert_pop2!(vm, CMDBAR_ABORT, ctx);
888 assert_eq!(vm.mode(), EmacsMode::Insert);
889
890 vm.input_key(ctl!('s'));
892 assert_pop2!(vm, cmdbar_search("/"), ctx);
893 assert_eq!(vm.mode(), EmacsMode::Search);
894
895 vm.input_key(ctl!('g'));
897 assert_pop2!(vm, CMDBAR_ABORT, ctx);
898 assert_eq!(vm.mode(), EmacsMode::Insert);
899 }
900
901 #[test]
902 fn test_count() {
903 let mut vm: EmacsMachine<TerminalKey> = default_emacs_keys();
904 let mut ctx = mkctx();
905
906 assert_eq!(vm.mode(), EmacsMode::Insert);
908
909 vm.input_key(key!('2'));
911 assert_pop2!(vm, typechar!('2'), ctx);
912
913 vm.input_key(key!('2', KeyModifiers::ALT));
915 assert_eq!(vm.pop(), None);
916
917 vm.input_key(key!('2'));
919 assert_eq!(vm.pop(), None);
920
921 ctx.action.count = Some(22);
923 vm.input_key(key!(' '));
924 assert_pop2!(vm, typechar!(' '), ctx);
925
926 ctx.action.count = Some(22);
928 vm.input_key(ctl!('u'));
929 vm.input_key(key!('2'));
930 vm.input_key(key!('2'));
931 vm.input_key(key!(' '));
932 assert_pop2!(vm, typechar!(' '), ctx);
933
934 ctx.action.count = Some(64);
936 vm.input_key(ctl!('u'));
937 vm.input_key(ctl!('u'));
938 vm.input_key(ctl!('u'));
939 vm.input_key(key!(' '));
940 assert_pop2!(vm, typechar!(' '), ctx);
941 }
942
943 #[test]
944 fn test_repeat_action() {
945 let mut vm: EmacsMachine<TerminalKey> = default_emacs_keys();
946 let ctx = mkctx();
947
948 assert_eq!(vm.mode(), EmacsMode::Insert);
950
951 vm.input_key(key!('a'));
953 vm.input_key(key!('b'));
954 vm.input_key(key!('c'));
955 assert_pop1!(vm, typechar!('a'), ctx);
956 assert_pop1!(vm, typechar!('b'), ctx);
957 assert_pop2!(vm, typechar!('c'), ctx);
958
959 vm.input_key(ctl!('x'));
961 vm.input_key(key!('z'));
962 assert_eq!(vm.state().persist.repeating, true);
963 assert_pop2!(vm, Action::Repeat(RepeatType::LastAction), ctx);
964
965 vm.repeat(RepeatType::LastAction, Some(ctx.clone().into()));
967 assert_pop2!(vm, typechar!('c'), ctx);
968
969 vm.input_key(key!('z'));
971 assert_pop2!(vm, Action::Repeat(RepeatType::LastAction), ctx);
972 assert_eq!(vm.state().persist.repeating, true);
973
974 vm.repeat(RepeatType::LastAction, Some(ctx.clone().into()));
976 assert_pop2!(vm, typechar!('c'), ctx);
977
978 vm.input_key(key!('z'));
980 assert_pop2!(vm, Action::Repeat(RepeatType::LastAction), ctx);
981 assert_eq!(vm.state().persist.repeating, true);
982
983 vm.repeat(RepeatType::LastAction, Some(ctx.clone().into()));
985 assert_pop2!(vm, typechar!('c'), ctx);
986
987 vm.input_key(key!('d'));
989 assert_pop2!(vm, typechar!('d'), ctx);
990 assert_eq!(vm.state().persist.repeating, false);
991
992 vm.input_key(key!('z'));
994 assert_pop2!(vm, typechar!('z'), ctx);
995 assert_eq!(vm.state().persist.repeating, false);
996
997 vm.input_key(ctl!('x'));
999 vm.input_key(key!('z'));
1000 assert_pop2!(vm, Action::Repeat(RepeatType::LastAction), ctx);
1001 assert_eq!(vm.state().persist.repeating, true);
1002
1003 vm.repeat(RepeatType::LastAction, Some(ctx.clone().into()));
1005 assert_pop2!(vm, typechar!('z'), ctx);
1006
1007 vm.input_key(key!('z'));
1009 assert_pop2!(vm, Action::Repeat(RepeatType::LastAction), ctx);
1010 assert_eq!(vm.state().persist.repeating, true);
1011
1012 vm.repeat(RepeatType::LastAction, Some(ctx.clone().into()));
1014 assert_pop2!(vm, typechar!('z'), ctx);
1015
1016 vm.input_key(key!(KeyCode::Right));
1018 assert_pop2!(vm, mv!(MoveType::Column(MoveDir1D::Next, true)), ctx);
1019 assert_eq!(vm.state().persist.repeating, false);
1020
1021 vm.input_key(key!('z'));
1023 assert_pop2!(vm, typechar!('z'), ctx);
1024 assert_eq!(vm.state().persist.repeating, false);
1025 }
1026}