summaryrefslogtreecommitdiffstats
path: root/src/input/key_bindings.rs
blob: 9bc5274f95249af477575ca1d3b072bb99ad3119 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
use super::{Event, KeyModifiers};
use crate::input::{KeyCode, KeyEvent};

#[derive(Debug)]
pub struct KeyBindings {
	pub(crate) abort: Vec<Event>,
	pub(crate) action_break: Vec<Event>,
	pub(crate) action_drop: Vec<Event>,
	pub(crate) action_edit: Vec<Event>,
	pub(crate) action_fixup: Vec<Event>,
	pub(crate) action_pick: Vec<Event>,
	pub(crate) action_reword: Vec<Event>,
	pub(crate) action_squash: Vec<Event>,
	pub(crate) confirm_yes: Vec<Event>,
	pub(crate) edit: Vec<Event>,
	pub(crate) force_abort: Vec<Event>,
	pub(crate) force_rebase: Vec<Event>,
	pub(crate) help: Vec<Event>,
	pub(crate) insert_line: Vec<Event>,
	pub(crate) move_down: Vec<Event>,
	pub(crate) move_down_step: Vec<Event>,
	pub(crate) move_end: Vec<Event>,
	pub(crate) move_home: Vec<Event>,
	pub(crate) move_left: Vec<Event>,
	pub(crate) move_right: Vec<Event>,
	pub(crate) move_selection_down: Vec<Event>,
	pub(crate) move_selection_up: Vec<Event>,
	pub(crate) move_up: Vec<Event>,
	pub(crate) move_up_step: Vec<Event>,
	pub(crate) open_in_external_editor: Vec<Event>,
	pub(crate) rebase: Vec<Event>,
	pub(crate) redo: Vec<Event>,
	pub(crate) remove_line: Vec<Event>,
	pub(crate) show_commit: Vec<Event>,
	pub(crate) show_diff: Vec<Event>,
	pub(crate) toggle_visual_mode: Vec<Event>,
	pub(crate) undo: Vec<Event>,
}

fn map_keybindings(bindings: &[String]) -> Vec<Event> {
	bindings
		.iter()
		.map(|b| {
			let mut key = String::from(b);
			let mut modifiers = KeyModifiers::empty();
			if key.contains("Control") {
				key = key.replace("Control", "");
				modifiers.insert(KeyModifiers::CONTROL);
			}
			if key.contains("Alt") {
				key = key.replace("Alt", "");
				modifiers.insert(KeyModifiers::ALT);
			}
			if key.contains("Shift") {
				key = key.replace("Shift", "");
				modifiers.insert(KeyModifiers::SHIFT);
			}

			let code = match key.as_str() {
				"Backspace" => KeyCode::Backspace,
				"BackTab" => KeyCode::BackTab,
				"Delete" => KeyCode::Delete,
				"Down" => KeyCode::Down,
				"End" => KeyCode::End,
				"Enter" => KeyCode::Enter,
				"Esc" => KeyCode::Esc,
				"Home" => KeyCode::Home,
				"Insert" => KeyCode::Insert,
				"Left" => KeyCode::Left,
				"PageDown" => KeyCode::PageDown,
				"PageUp" => KeyCode::PageUp,
				"Right" => KeyCode::Right,
				"Tab" => KeyCode::Tab,
				"Up" => KeyCode::Up,
				// assume that this is an F key
				k if k.len() > 1 => {
					let key_number = k[1..].parse::<u8>().unwrap_or(1);
					KeyCode::F(key_number)
				},
				k => {
					let c = k.chars().next().unwrap();
					KeyCode::Char(c)
				},
			};
			Event::Key(KeyEvent::new(code, modifiers))
		})
		.collect()
}

impl KeyBindings {
	pub fn new(key_bindings: &crate::config::KeyBindings) -> Self {
		Self {
			abort: map_keybindings(&key_bindings.abort),
			action_break: map_keybindings(&key_bindings.action_break),
			action_drop: map_keybindings(&key_bindings.action_drop),
			action_edit: map_keybindings(&key_bindings.action_edit),
			action_fixup: map_keybindings(&key_bindings.action_fixup),
			action_pick: map_keybindings(&key_bindings.action_pick),
			action_reword: map_keybindings(&key_bindings.action_reword),
			action_squash: map_keybindings(&key_bindings.action_squash),
			edit: map_keybindings(&key_bindings.edit),
			force_abort: map_keybindings(&key_bindings.force_abort),
			force_rebase: map_keybindings(&key_bindings.force_rebase),
			help: map_keybindings(&key_bindings.help),
			insert_line: map_keybindings(&key_bindings.insert_line),
			move_down: map_keybindings(&key_bindings.move_down),
			move_down_step: map_keybindings(&key_bindings.move_down_step),
			move_end: map_keybindings(&key_bindings.move_end),
			move_home: map_keybindings(&key_bindings.move_home),
			move_left: map_keybindings(&key_bindings.move_left),
			move_right: map_keybindings(&key_bindings.move_right),
			move_selection_down: map_keybindings(&key_bindings.move_selection_down),
			move_selection_up: map_keybindings(&key_bindings.move_selection_up),
			move_up: map_keybindings(&key_bindings.move_up),
			move_up_step: map_keybindings(&key_bindings.move_up_step),
			open_in_external_editor: map_keybindings(&key_bindings.open_in_external_editor),
			rebase: map_keybindings(&key_bindings.rebase),
			redo: map_keybindings(&key_bindings.redo),
			remove_line: map_keybindings(&key_bindings.remove_line),
			show_commit: map_keybindings(&key_bindings.show_commit),
			show_diff: map_keybindings(&key_bindings.show_diff),
			toggle_visual_mode: map_keybindings(&key_bindings.toggle_visual_mode),
			undo: map_keybindings(&key_bindings.undo),
			confirm_yes: map_keybindings(&key_bindings.confirm_yes),
		}
	}
}

#[cfg(test)]
mod tests {
	use rstest::rstest;

	use super::*;
	use crate::config::testutil::create_config;

	#[test]
	fn new() {
		KeyBindings::new(&create_config().key_bindings);
	}

	#[test]
	fn map_keybindings_with_modifiers() {
		assert_eq!(map_keybindings(&[String::from("ControlAltShifta")]), vec![Event::Key(
			KeyEvent {
				code: KeyCode::Char('a'),
				modifiers: KeyModifiers::all()
			}
		)]);
	}

	#[rstest(
		binding,
		key_code,
		case::backspace("Backspace", KeyCode::Backspace),
		case::back_tab("BackTab", KeyCode::BackTab),
		case::delete("Delete", KeyCode::Delete),
		case::down("Down", KeyCode::Down),
		case::end("End", KeyCode::End),
		case::enter("Enter", KeyCode::Enter),
		case::esc("Esc", KeyCode::Esc),
		case::home("Home", KeyCode::Home),
		case::insert("Insert", KeyCode::Insert),
		case::left("Left", KeyCode::Left),
		case::page_down("PageDown", KeyCode::PageDown),
		case::page_up("PageUp", KeyCode::PageUp),
		case::right("Right", KeyCode::Right),
		case::tab("Tab", KeyCode::Tab),
		case::up("Up", KeyCode::Up),
		case::function_in_range("F10", KeyCode::F(10)),
		case::function_out_of_range("F10000", KeyCode::F(1)),
		case::char("a", KeyCode::Char('a'))
	)]
	fn map_keybindings_key_code(binding: &str, key_code: KeyCode) {
		assert_eq!(map_keybindings(&[String::from(binding)]), vec![Event::from(key_code)]);
	}
}