1 /*
2 * Copyright 2018 Jiri Techet <techet@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "cmd-runner.h"
20 #include "utils.h"
21
22 #include "cmds/motion.h"
23 #include "cmds/motion-word.h"
24 #include "cmds/txtobjs.h"
25 #include "cmds/changemode.h"
26 #include "cmds/edit.h"
27 #include "cmds/special.h"
28
29 #include <gdk/gdkkeysyms.h>
30
31 typedef struct {
32 Cmd cmd;
33 guint key1;
34 guint key2;
35 guint modif1;
36 guint modif2;
37 gboolean param;
38 gboolean needs_selection;
39 } CmdDef;
40
41
42 #define ARROW_MOTIONS \
43 /* left */ \
44 {cmd_goto_left, GDK_KEY_Left, 0, 0, 0, FALSE, FALSE}, \
45 {cmd_goto_left, GDK_KEY_KP_Left, 0, 0, 0, FALSE, FALSE}, \
46 {cmd_goto_left, GDK_KEY_leftarrow, 0, 0, 0, FALSE, FALSE}, \
47 /* right */ \
48 {cmd_goto_right, GDK_KEY_Right, 0, 0, 0, FALSE, FALSE}, \
49 {cmd_goto_right, GDK_KEY_KP_Right, 0, 0, 0, FALSE, FALSE}, \
50 {cmd_goto_right, GDK_KEY_rightarrow, 0, 0, 0, FALSE, FALSE}, \
51 /* up */ \
52 {cmd_goto_up, GDK_KEY_Up, 0, 0, 0, FALSE, FALSE}, \
53 {cmd_goto_up, GDK_KEY_KP_Up, 0, 0, 0, FALSE, FALSE}, \
54 {cmd_goto_up, GDK_KEY_uparrow, 0, 0, 0, FALSE, FALSE}, \
55 /* down */ \
56 {cmd_goto_down, GDK_KEY_Down, 0, 0, 0, FALSE, FALSE}, \
57 {cmd_goto_down, GDK_KEY_KP_Down, 0, 0, 0, FALSE, FALSE}, \
58 {cmd_goto_down, GDK_KEY_downarrow, 0, 0, 0, FALSE, FALSE}, \
59 /* goto next word */ \
60 {cmd_goto_next_word, GDK_KEY_Right, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
61 {cmd_goto_next_word, GDK_KEY_Right, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
62 {cmd_goto_next_word, GDK_KEY_KP_Right, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
63 {cmd_goto_next_word, GDK_KEY_KP_Right, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
64 {cmd_goto_next_word, GDK_KEY_rightarrow, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
65 {cmd_goto_next_word, GDK_KEY_rightarrow, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
66 /* goto prev word */ \
67 {cmd_goto_previous_word, GDK_KEY_Left, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
68 {cmd_goto_previous_word, GDK_KEY_Left, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
69 {cmd_goto_previous_word, GDK_KEY_KP_Left, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
70 {cmd_goto_previous_word, GDK_KEY_KP_Left, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
71 {cmd_goto_previous_word, GDK_KEY_leftarrow, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
72 {cmd_goto_previous_word, GDK_KEY_leftarrow, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
73 /* page up/down */ \
74 {cmd_goto_page_up, GDK_KEY_Up, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
75 {cmd_goto_page_up, GDK_KEY_KP_Up, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
76 {cmd_goto_page_up, GDK_KEY_uparrow, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
77 {cmd_goto_page_down, GDK_KEY_Down, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
78 {cmd_goto_page_down, GDK_KEY_KP_Down, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
79 {cmd_goto_page_down, GDK_KEY_downarrow, 0, GDK_SHIFT_MASK, 0, FALSE, FALSE}, \
80 /* END */
81
82
83 #define MOVEMENT_CMDS \
84 ARROW_MOTIONS \
85 /* left */ \
86 {cmd_goto_left, GDK_KEY_h, 0, 0, 0, FALSE, FALSE}, \
87 {cmd_goto_left, GDK_KEY_h, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
88 {cmd_goto_left, GDK_KEY_BackSpace, 0, 0, 0, FALSE, FALSE}, \
89 /* right */ \
90 {cmd_goto_right, GDK_KEY_l, 0, 0, 0, FALSE, FALSE}, \
91 {cmd_goto_right, GDK_KEY_space, 0, 0, 0, FALSE, FALSE}, \
92 /* up */ \
93 {cmd_goto_up, GDK_KEY_k, 0, 0, 0, FALSE, FALSE}, \
94 {cmd_goto_up, GDK_KEY_p, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
95 {cmd_goto_up_nonempty, GDK_KEY_minus, 0, 0, 0, FALSE, FALSE}, \
96 {cmd_goto_up_nonempty, GDK_KEY_KP_Subtract, 0, 0, 0, FALSE, FALSE}, \
97 /* down */ \
98 {cmd_goto_down, GDK_KEY_j, 0, 0, 0, FALSE, FALSE}, \
99 {cmd_goto_down, GDK_KEY_j, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
100 {cmd_goto_down, GDK_KEY_n, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
101 {cmd_goto_down_nonempty, GDK_KEY_plus, 0, 0, 0, FALSE, FALSE}, \
102 {cmd_goto_down_nonempty, GDK_KEY_KP_Add, 0, 0, 0, FALSE, FALSE}, \
103 {cmd_goto_down_nonempty, GDK_KEY_m, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
104 {cmd_goto_down_nonempty, GDK_KEY_Return, 0, 0, 0, FALSE, FALSE}, \
105 {cmd_goto_down_nonempty, GDK_KEY_KP_Enter, 0, 0, 0, FALSE, FALSE}, \
106 {cmd_goto_down_nonempty, GDK_KEY_ISO_Enter, 0, 0, 0, FALSE, FALSE}, \
107 {cmd_goto_down_one_less_nonempty, GDK_KEY_underscore, 0, 0, 0, FALSE, FALSE}, \
108 /* line beginning */ \
109 {cmd_goto_line_start, GDK_KEY_0, 0, 0, 0, FALSE, FALSE}, \
110 {cmd_goto_line_start, GDK_KEY_Home, 0, 0, 0, FALSE, FALSE}, \
111 {cmd_goto_line_start_nonempty, GDK_KEY_asciicircum, 0, 0, 0, FALSE, FALSE}, \
112 /* line end */ \
113 {cmd_goto_line_end, GDK_KEY_dollar, 0, 0, 0, FALSE, FALSE}, \
114 {cmd_goto_line_end, GDK_KEY_End, 0, 0, 0, FALSE, FALSE}, \
115 {cmd_goto_column, GDK_KEY_bar, 0, 0, 0, FALSE, FALSE}, \
116 /* find character */ \
117 {cmd_goto_next_char, GDK_KEY_f, 0, 0, 0, TRUE, FALSE}, \
118 {cmd_goto_prev_char, GDK_KEY_F, 0, 0, 0, TRUE, FALSE}, \
119 {cmd_goto_next_char_before, GDK_KEY_t, 0, 0, 0, TRUE, FALSE}, \
120 {cmd_goto_prev_char_before, GDK_KEY_T, 0, 0, 0, TRUE, FALSE}, \
121 {cmd_goto_char_repeat, GDK_KEY_semicolon, 0, 0, 0, FALSE, FALSE}, \
122 {cmd_goto_char_repeat_opposite, GDK_KEY_comma, 0, 0, 0, FALSE, FALSE}, \
123 /* goto line */ \
124 {cmd_goto_line_last, GDK_KEY_G, 0, 0, 0, FALSE, FALSE}, \
125 {cmd_goto_line_last, GDK_KEY_End, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
126 {cmd_goto_line_last, GDK_KEY_KP_End, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
127 {cmd_goto_line, GDK_KEY_g, GDK_KEY_g, 0, 0, FALSE, FALSE}, \
128 {cmd_goto_line, GDK_KEY_Home, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
129 {cmd_goto_line, GDK_KEY_KP_Home, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
130 {cmd_goto_doc_percentage, GDK_KEY_percent, 0, 0, 0, FALSE, FALSE}, \
131 /* goto next word */ \
132 {cmd_goto_next_word, GDK_KEY_w, 0, 0, 0, FALSE, FALSE}, \
133 {cmd_goto_next_word_space, GDK_KEY_W, 0, 0, 0, FALSE, FALSE}, \
134 {cmd_goto_next_word_end, GDK_KEY_e, 0, 0, 0, FALSE, FALSE}, \
135 {cmd_goto_next_word_end_space, GDK_KEY_E, 0, 0, 0, FALSE, FALSE}, \
136 /* goto prev word */ \
137 {cmd_goto_previous_word, GDK_KEY_b, 0, 0, 0, FALSE, FALSE}, \
138 {cmd_goto_previous_word_space, GDK_KEY_B, 0, 0, 0, FALSE, FALSE}, \
139 {cmd_goto_previous_word_end, GDK_KEY_g, GDK_KEY_e, 0, 0, FALSE, FALSE}, \
140 {cmd_goto_previous_word_end_space, GDK_KEY_g, GDK_KEY_E, 0, 0, FALSE, FALSE}, \
141 /* various motions */ \
142 {cmd_goto_matching_brace, GDK_KEY_percent, 0, 0, 0, FALSE, FALSE}, \
143 {cmd_goto_screen_top, GDK_KEY_H, 0, 0, 0, FALSE, FALSE}, \
144 {cmd_goto_screen_middle, GDK_KEY_M, 0, 0, 0, FALSE, FALSE}, \
145 {cmd_goto_screen_bottom, GDK_KEY_L, 0, 0, 0, FALSE, FALSE}, \
146 /* page up/down */ \
147 {cmd_goto_page_down, GDK_KEY_f, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
148 {cmd_goto_page_down, GDK_KEY_Page_Down, 0, 0, 0, FALSE, FALSE}, \
149 {cmd_goto_page_down, GDK_KEY_KP_Page_Down, 0, 0, 0, FALSE, FALSE}, \
150 {cmd_goto_page_up, GDK_KEY_b, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
151 {cmd_goto_page_up, GDK_KEY_Page_Up, 0, 0, 0, FALSE, FALSE}, \
152 {cmd_goto_page_up, GDK_KEY_KP_Page_Up, 0, 0, 0, FALSE, FALSE}, \
153 {cmd_goto_halfpage_down, GDK_KEY_d, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
154 {cmd_goto_halfpage_up, GDK_KEY_u, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
155 /* scrolling */ \
156 {cmd_scroll_down, GDK_KEY_e, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
157 {cmd_scroll_up, GDK_KEY_y, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE}, \
158 {cmd_scroll_center, GDK_KEY_z, GDK_KEY_z, 0, 0, FALSE, FALSE}, \
159 {cmd_scroll_top, GDK_KEY_z, GDK_KEY_t, 0, 0, FALSE, FALSE}, \
160 {cmd_scroll_bottom, GDK_KEY_z, GDK_KEY_b, 0, 0, FALSE, FALSE}, \
161 {cmd_scroll_center_nonempty, GDK_KEY_z, GDK_KEY_period, 0, 0, FALSE, FALSE}, \
162 {cmd_scroll_top_nonempty, GDK_KEY_z, GDK_KEY_Return, 0, 0, FALSE, FALSE}, \
163 {cmd_scroll_top_nonempty, GDK_KEY_z, GDK_KEY_KP_Enter, 0, 0, FALSE, FALSE}, \
164 {cmd_scroll_top_nonempty, GDK_KEY_z, GDK_KEY_ISO_Enter, 0, 0, FALSE, FALSE}, \
165 {cmd_scroll_top_next_nonempty, GDK_KEY_z, GDK_KEY_plus, 0, 0, FALSE, FALSE}, \
166 {cmd_scroll_bottom_nonempty, GDK_KEY_z, GDK_KEY_minus, 0, 0, FALSE, FALSE}, \
167 /* END */
168
169
170 /* From the above commands, these commands also include the character
171 * where the destionation movement ends for motion commands (e.g. 'de' will
172 * delete the word including the last character) */
173 CmdDef include_dest_char_movement_cmds[] = {
174 {cmd_goto_next_char, GDK_KEY_f, 0, 0, 0, TRUE, FALSE},
175 {cmd_goto_next_char_before, GDK_KEY_t, 0, 0, 0, TRUE, FALSE},
176 {cmd_goto_next_word_end, GDK_KEY_e, 0, 0, 0, FALSE, FALSE},
177 {cmd_goto_next_word_end_space, GDK_KEY_E, 0, 0, 0, FALSE, FALSE},
178 {cmd_goto_previous_word, GDK_KEY_b, 0, 0, 0, FALSE, FALSE},
179 {cmd_goto_previous_word_space, GDK_KEY_B, 0, 0, 0, FALSE, FALSE},
180 {cmd_goto_matching_brace, GDK_KEY_percent, 0, 0, 0, FALSE, FALSE},
181 {NULL, 0, 0, 0, 0, FALSE, FALSE}
182 };
183
184
185 CmdDef movement_cmds[] = {
186 MOVEMENT_CMDS
187 {NULL, 0, 0, 0, 0, FALSE, FALSE}
188 };
189
190
191 #define OPERATOR_CMDS \
192 {cmd_enter_command_cut_sel, GDK_KEY_d, 0, 0, 0, FALSE, TRUE}, \
193 {cmd_enter_command_copy_sel, GDK_KEY_y, 0, 0, 0, FALSE, TRUE}, \
194 {cmd_enter_insert_cut_sel, GDK_KEY_c, 0, 0, 0, FALSE, TRUE}, \
195 {cmd_unindent_sel, GDK_KEY_less, 0, 0, 0, FALSE, TRUE}, \
196 {cmd_indent_sel, GDK_KEY_greater, 0, 0, 0, FALSE, TRUE}, \
197 {cmd_switch_case, GDK_KEY_g, GDK_KEY_asciitilde, 0, 0, FALSE, TRUE}, \
198 {cmd_switch_case, GDK_KEY_asciitilde, 0, 0, 0, FALSE, TRUE}, \
199 {cmd_upper_case, GDK_KEY_g, GDK_KEY_U, 0, 0, FALSE, TRUE}, \
200 {cmd_lower_case, GDK_KEY_g, GDK_KEY_u, 0, 0, FALSE, TRUE}, \
201 /* END */
202
203
204 CmdDef operator_cmds[] = {
205 OPERATOR_CMDS
206 {NULL, 0, 0, 0, 0, FALSE, FALSE}
207 };
208
209
210 #define TEXT_OBJECT_CMDS \
211 /* inclusive */ \
212 {cmd_select_quotedbl, GDK_KEY_a, GDK_KEY_quotedbl, 0, 0, FALSE, FALSE}, \
213 {cmd_select_quoteleft, GDK_KEY_a, GDK_KEY_quoteleft, 0, 0, FALSE, FALSE}, \
214 {cmd_select_apostrophe, GDK_KEY_a, GDK_KEY_apostrophe, 0, 0, FALSE, FALSE}, \
215 {cmd_select_brace, GDK_KEY_a, GDK_KEY_braceleft, 0, 0, FALSE, FALSE}, \
216 {cmd_select_brace, GDK_KEY_a, GDK_KEY_braceright, 0, 0, FALSE, FALSE}, \
217 {cmd_select_brace, GDK_KEY_a, GDK_KEY_B, 0, 0, FALSE, FALSE}, \
218 {cmd_select_paren, GDK_KEY_a, GDK_KEY_parenleft, 0, 0, FALSE, FALSE}, \
219 {cmd_select_paren, GDK_KEY_a, GDK_KEY_parenright, 0, 0, FALSE, FALSE}, \
220 {cmd_select_paren, GDK_KEY_a, GDK_KEY_b, 0, 0, FALSE, FALSE}, \
221 {cmd_select_less, GDK_KEY_a, GDK_KEY_less, 0, 0, FALSE, FALSE}, \
222 {cmd_select_less, GDK_KEY_a, GDK_KEY_greater, 0, 0, FALSE, FALSE}, \
223 {cmd_select_bracket, GDK_KEY_a, GDK_KEY_bracketleft, 0, 0, FALSE, FALSE}, \
224 {cmd_select_bracket, GDK_KEY_a, GDK_KEY_bracketright, 0, 0, FALSE, FALSE}, \
225 /* inner */ \
226 {cmd_select_quotedbl_inner, GDK_KEY_i, GDK_KEY_quotedbl, 0, 0, FALSE, FALSE}, \
227 {cmd_select_quoteleft_inner, GDK_KEY_i, GDK_KEY_quoteleft, 0, 0, FALSE, FALSE}, \
228 {cmd_select_apostrophe_inner, GDK_KEY_i, GDK_KEY_apostrophe, 0, 0, FALSE, FALSE}, \
229 {cmd_select_brace_inner, GDK_KEY_i, GDK_KEY_braceleft, 0, 0, FALSE, FALSE}, \
230 {cmd_select_brace_inner, GDK_KEY_i, GDK_KEY_braceright, 0, 0, FALSE, FALSE}, \
231 {cmd_select_brace_inner, GDK_KEY_i, GDK_KEY_B, 0, 0, FALSE, FALSE}, \
232 {cmd_select_paren_inner, GDK_KEY_i, GDK_KEY_parenleft, 0, 0, FALSE, FALSE}, \
233 {cmd_select_paren_inner, GDK_KEY_i, GDK_KEY_parenright, 0, 0, FALSE, FALSE}, \
234 {cmd_select_paren_inner, GDK_KEY_i, GDK_KEY_b, 0, 0, FALSE, FALSE}, \
235 {cmd_select_less_inner, GDK_KEY_i, GDK_KEY_less, 0, 0, FALSE, FALSE}, \
236 {cmd_select_less_inner, GDK_KEY_i, GDK_KEY_greater, 0, 0, FALSE, FALSE}, \
237 {cmd_select_bracket_inner, GDK_KEY_i, GDK_KEY_bracketleft, 0, 0, FALSE, FALSE}, \
238 {cmd_select_bracket_inner, GDK_KEY_i, GDK_KEY_bracketright, 0, 0, FALSE, FALSE}, \
239 /* END */
240
241
242 CmdDef text_object_cmds[] = {
243 TEXT_OBJECT_CMDS
244 {NULL, 0, 0, 0, 0, FALSE, FALSE}
245 };
246
247
248 #define EDIT_CMDS \
249 /* deletion */ \
250 {cmd_delete_char_copy, GDK_KEY_x, 0, 0, 0, FALSE, FALSE}, \
251 {cmd_delete_char_copy, GDK_KEY_Delete, 0, 0, 0, FALSE, FALSE}, \
252 {cmd_delete_char_copy, GDK_KEY_KP_Delete, 0, 0, 0, FALSE, FALSE}, \
253 {cmd_delete_char_back_copy, GDK_KEY_X, 0, 0, 0, FALSE, FALSE}, \
254 {cmd_delete_line, GDK_KEY_d, GDK_KEY_d, 0, 0, FALSE, FALSE}, \
255 {cmd_clear_right, GDK_KEY_D, 0, 0, 0, FALSE, FALSE}, \
256 /* copy/paste */ \
257 {cmd_copy_line, GDK_KEY_y, GDK_KEY_y, 0, 0, FALSE, FALSE}, \
258 {cmd_copy_line, GDK_KEY_Y, 0, 0, 0, FALSE, FALSE}, \
259 {cmd_paste_after, GDK_KEY_p, 0, 0, 0, FALSE, FALSE}, \
260 {cmd_paste_before, GDK_KEY_P, 0, 0, 0, FALSE, FALSE}, \
261 /* changing text */ \
262 {cmd_enter_insert_cut_line, GDK_KEY_c, GDK_KEY_c, 0, 0, FALSE, FALSE}, \
263 {cmd_enter_insert_cut_line, GDK_KEY_S, 0, 0, 0, FALSE, FALSE}, \
264 {cmd_enter_insert_clear_right, GDK_KEY_C, 0, 0, 0, FALSE, FALSE}, \
265 {cmd_enter_insert_delete_char, GDK_KEY_s, 0, 0, 0, FALSE, FALSE}, \
266 {cmd_replace_char, GDK_KEY_r, 0, 0, 0, TRUE, FALSE}, \
267 {cmd_switch_case, GDK_KEY_asciitilde, 0, 0, 0, FALSE, FALSE}, \
268 {cmd_indent, GDK_KEY_greater, GDK_KEY_greater, 0, 0, FALSE, FALSE}, \
269 {cmd_unindent, GDK_KEY_less, GDK_KEY_less, 0, 0, FALSE, FALSE}, \
270 {cmd_repeat_subst, GDK_KEY_ampersand, 0, 0, 0, FALSE, FALSE}, \
271 {cmd_join_lines, GDK_KEY_J, 0, 0, 0, FALSE, FALSE}, \
272 /* undo/redo */ \
273 {cmd_undo, GDK_KEY_U, 0, 0, 0, FALSE, FALSE}, \
274 {cmd_undo, GDK_KEY_u, 0, 0, 0, FALSE, FALSE}, \
275 {cmd_redo, GDK_KEY_r, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
276
277 CmdDef edit_cmds[] = {
278 EDIT_CMDS
279 OPERATOR_CMDS
280 {NULL, 0, 0, 0, 0, FALSE, FALSE}
281 };
282
283
284 #define ENTER_EX_CMDS \
285 {cmd_enter_ex, GDK_KEY_colon, 0, 0, 0, FALSE, FALSE}, \
286 {cmd_enter_ex, GDK_KEY_slash, 0, 0, 0, FALSE, FALSE}, \
287 {cmd_enter_ex, GDK_KEY_KP_Divide, 0, 0, 0, FALSE, FALSE}, \
288 {cmd_enter_ex, GDK_KEY_question, 0, 0, 0, FALSE, FALSE}, \
289 /* END */
290
291
292 #define SEARCH_CMDS \
293 {cmd_search_next, GDK_KEY_n, 0, 0, 0, FALSE, FALSE}, \
294 {cmd_search_next, GDK_KEY_N, 0, 0, 0, FALSE, FALSE}, \
295 {cmd_search_current_next, GDK_KEY_asterisk, 0, 0, 0, FALSE, FALSE}, \
296 {cmd_search_current_next, GDK_KEY_KP_Multiply, 0, 0, 0, FALSE, FALSE}, \
297 {cmd_search_current_prev, GDK_KEY_numbersign, 0, 0, 0, FALSE, FALSE}, \
298 /* END */
299
300 CmdDef cmd_mode_cmds[] = {
301 /* enter insert mode */
302 {cmd_enter_insert_after, GDK_KEY_a, 0, 0, 0, FALSE, FALSE},
303 {cmd_enter_insert_line_end, GDK_KEY_A, 0, 0, 0, FALSE, FALSE},
304 {cmd_enter_insert, GDK_KEY_i, 0, 0, 0, FALSE, FALSE},
305 {cmd_enter_insert, GDK_KEY_Insert, 0, 0, 0, FALSE, FALSE},
306 {cmd_enter_insert, GDK_KEY_KP_Insert, 0, 0, 0, FALSE, FALSE},
307 {cmd_enter_insert_line_start_nonempty, GDK_KEY_I, 0, 0, 0, FALSE, FALSE},
308 {cmd_enter_insert_line_start, GDK_KEY_g, GDK_KEY_I, 0, 0, FALSE, FALSE},
309 {cmd_enter_insert_next_line, GDK_KEY_o, 0, 0, 0, FALSE, FALSE},
310 {cmd_enter_insert_prev_line, GDK_KEY_O, 0, 0, 0, FALSE, FALSE},
311 /* enter replace mode */
312 {cmd_enter_replace, GDK_KEY_R, 0, 0, 0, FALSE, FALSE},
313 /* enter visual mode */
314 {cmd_enter_visual, GDK_KEY_v, 0, 0, 0, FALSE, FALSE},
315 {cmd_enter_visual_line, GDK_KEY_V, 0, 0, 0, FALSE, FALSE},
316
317 /* special */
318 {cmd_repeat_last_command, GDK_KEY_period, 0, 0, 0, FALSE, FALSE},
319 {cmd_repeat_last_command, GDK_KEY_KP_Decimal, 0, 0, 0, FALSE, FALSE},
320 {cmd_enter_command, GDK_KEY_Escape, 0, 0, 0, FALSE, FALSE},
321 {cmd_nop, GDK_KEY_Insert, 0, 0, 0, FALSE, FALSE},
322 {cmd_nop, GDK_KEY_KP_Insert, 0, 0, 0, FALSE, FALSE},
323 {cmd_write_exit, GDK_KEY_Z, GDK_KEY_Z, 0, 0, FALSE, FALSE},
324 {cmd_force_exit, GDK_KEY_Z, GDK_KEY_Q, 0, 0, FALSE, FALSE},
325
326 EDIT_CMDS
327 OPERATOR_CMDS
328 SEARCH_CMDS
329 MOVEMENT_CMDS
330 TEXT_OBJECT_CMDS
331 ENTER_EX_CMDS
332
333 {NULL, 0, 0, 0, 0, FALSE, FALSE}
334 };
335
336
337 CmdDef vis_mode_cmds[] = {
338 {cmd_enter_command, GDK_KEY_Escape, 0, 0, 0, FALSE, FALSE},
339 {cmd_enter_command, GDK_KEY_c, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
340 {cmd_enter_visual, GDK_KEY_v, 0, 0, 0, FALSE, FALSE},
341 {cmd_enter_visual_line, GDK_KEY_V, 0, 0, 0, FALSE, FALSE},
342
343 {cmd_enter_insert_cut_line_sel, GDK_KEY_C, 0, 0, 0, FALSE, FALSE},
344 {cmd_enter_insert_cut_line_sel, GDK_KEY_S, 0, 0, 0, FALSE, FALSE},
345 {cmd_enter_insert_cut_line_sel, GDK_KEY_R, 0, 0, 0, FALSE, FALSE},
346 {cmd_enter_command_cut_line_sel, GDK_KEY_D, 0, 0, 0, FALSE, FALSE},
347 {cmd_enter_command_cut_line_sel, GDK_KEY_X, 0, 0, 0, FALSE, FALSE},
348 {cmd_enter_command_cut_sel, GDK_KEY_x, 0, 0, 0, FALSE, FALSE},
349 {cmd_enter_command_cut_sel, GDK_KEY_Delete, 0, 0, 0, FALSE, FALSE},
350 {cmd_enter_command_cut_sel, GDK_KEY_KP_Delete, 0, 0, 0, FALSE, FALSE},
351 {cmd_enter_insert_cut_sel, GDK_KEY_s, 0, 0, 0, FALSE, FALSE},
352 {cmd_enter_command_copy_line_sel, GDK_KEY_Y, 0, 0, 0, FALSE, FALSE},
353
354 {cmd_upper_case, GDK_KEY_U, 0, 0, 0, FALSE, FALSE},
355 {cmd_lower_case, GDK_KEY_u, 0, 0, 0, FALSE, FALSE},
356 {cmd_join_lines_sel, GDK_KEY_J, 0, 0, 0, FALSE, FALSE},
357 {cmd_replace_char_sel, GDK_KEY_r, 0, 0, 0, TRUE, FALSE},
358
359 {cmd_swap_anchor, GDK_KEY_o, 0, 0, 0, FALSE, FALSE},
360 {cmd_swap_anchor, GDK_KEY_O, 0, 0, 0, FALSE, FALSE},
361 {cmd_nop, GDK_KEY_Insert, 0, 0, 0, FALSE, FALSE},
362 {cmd_nop, GDK_KEY_KP_Insert, 0, 0, 0, FALSE, FALSE},
363
364 SEARCH_CMDS
365 MOVEMENT_CMDS
366 TEXT_OBJECT_CMDS
367 OPERATOR_CMDS
368 ENTER_EX_CMDS
369
370 {NULL, 0, 0, 0, 0, FALSE, FALSE}
371 };
372
373
374 CmdDef ins_mode_cmds[] = {
375 {cmd_enter_command, GDK_KEY_Escape, 0, 0, 0, FALSE, FALSE},
376 {cmd_enter_command, GDK_KEY_c, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
377 {cmd_enter_command, GDK_KEY_bracketleft, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
378 {cmd_enter_command_single, GDK_KEY_o, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
379
380 {cmd_goto_line_last, GDK_KEY_End, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
381 {cmd_goto_line_last, GDK_KEY_KP_End, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
382 {cmd_goto_line, GDK_KEY_Home, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
383 {cmd_goto_line, GDK_KEY_KP_Home, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
384
385 {cmd_newline, GDK_KEY_m, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
386 {cmd_newline, GDK_KEY_j, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
387 {cmd_tab, GDK_KEY_i, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
388
389 {cmd_paste_inserted_text, GDK_KEY_a, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
390 {cmd_paste_inserted_text_leave_ins, GDK_KEY_at, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
391 /* it's enough to press Ctrl+2 instead of Ctrl+Shift+2 to get Ctrl+@ */
392 {cmd_paste_inserted_text_leave_ins, GDK_KEY_2, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
393
394 {cmd_delete_char, GDK_KEY_Delete, 0, 0, 0, FALSE, FALSE},
395 {cmd_delete_char, GDK_KEY_KP_Delete, 0, 0, 0, FALSE, FALSE},
396 {cmd_delete_char_back, GDK_KEY_h, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
397 {cmd_del_word_left, GDK_KEY_w, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
398 {cmd_indent_ins, GDK_KEY_t, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
399 {cmd_unindent_ins, GDK_KEY_d, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
400 {cmd_copy_char_from_below, GDK_KEY_e, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
401 {cmd_copy_char_from_above, GDK_KEY_y, 0, GDK_CONTROL_MASK, 0, FALSE, FALSE},
402 {cmd_paste_before, GDK_KEY_r, 0, GDK_CONTROL_MASK, 0, TRUE, FALSE},
403
404 ARROW_MOTIONS
405
406 {NULL, 0, 0, 0, 0, FALSE, FALSE}
407 };
408
409
is_in_cmd_group(CmdDef * cmds,CmdDef * def)410 static gboolean is_in_cmd_group(CmdDef *cmds, CmdDef *def)
411 {
412 int i;
413 for (i = 0; cmds[i].cmd != NULL; i++)
414 {
415 CmdDef *d = &cmds[i];
416 if (def->cmd == d->cmd && def->key1 == d->key1 && def->key2 == d->key2 &&
417 def->modif1 == d->modif1 && def->modif2 == d->modif2 && def->param == d->param)
418 return TRUE;
419 }
420 return FALSE;
421 }
422
423
key_equals(KeyPress * kp,guint key,guint modif)424 static gboolean key_equals(KeyPress *kp, guint key, guint modif)
425 {
426 return kp->key == key && (kp->modif & modif || kp->modif == modif);
427 }
428
429
430 /* is the current keypress the first character of a 2-keypress command? */
is_cmdpart(GSList * kpl,CmdDef * cmds)431 static gboolean is_cmdpart(GSList *kpl, CmdDef *cmds)
432 {
433 gint i;
434 KeyPress *curr = g_slist_nth_data(kpl, 0);
435
436 for (i = 0; cmds[i].cmd != NULL; i++)
437 {
438 CmdDef *cmd = &cmds[i];
439 if ((cmd->key2 != 0 || cmd->param) && key_equals(curr, cmd->key1, cmd->modif1))
440 return TRUE;
441 }
442
443 return FALSE;
444 }
445
446
is_printable(GSList * kpl)447 static gboolean is_printable(GSList *kpl)
448 {
449 guint mask = gtk_accelerator_get_default_mod_mask() & ~GDK_SHIFT_MASK;
450 KeyPress *kp = g_slist_nth_data(kpl, 0);
451
452 if (kp->modif & mask)
453 return FALSE;
454
455 return g_unichar_isprint(gdk_keyval_to_unicode(kp->key));
456 }
457
458
get_cmd_to_run(GSList * kpl,CmdDef * cmds,gboolean have_selection)459 static CmdDef *get_cmd_to_run(GSList *kpl, CmdDef *cmds, gboolean have_selection)
460 {
461 gint i;
462 KeyPress *curr = g_slist_nth_data(kpl, 0);
463 KeyPress *prev = g_slist_nth_data(kpl, 1);
464 GSList *below = g_slist_next(kpl);
465 ViMode mode = vi_get_mode();
466
467 if (!kpl)
468 return NULL;
469
470 // commands such as rc or fc (replace char c, find char c) which are specified
471 // by the previous character and current character is used as their parameter
472 if (prev != NULL && !kp_isdigit(prev))
473 {
474 for (i = 0; cmds[i].cmd != NULL; i++)
475 {
476 CmdDef *cmd = &cmds[i];
477 if (cmd->key2 == 0 && cmd->param &&
478 ((cmd->needs_selection && have_selection) || !cmd->needs_selection) &&
479 key_equals(prev, cmd->key1, cmd->modif1))
480 return cmd;
481 }
482 }
483
484 // 2-letter commands
485 if (prev != NULL && !kp_isdigit(prev))
486 {
487 for (i = 0; cmds[i].cmd != NULL; i++)
488 {
489 CmdDef *cmd = &cmds[i];
490 if (cmd->key2 != 0 && !cmd->param &&
491 ((cmd->needs_selection && have_selection) || !cmd->needs_selection) &&
492 key_equals(curr, cmd->key2, cmd->modif2) &&
493 key_equals(prev, cmd->key1, cmd->modif1))
494 return cmd;
495 }
496 }
497
498 // 1-letter commands
499 for (i = 0; cmds[i].cmd != NULL; i++)
500 {
501 CmdDef *cmd = &cmds[i];
502 if (cmd->key2 == 0 && !cmd->param &&
503 ((cmd->needs_selection && have_selection) || !cmd->needs_selection) &&
504 key_equals(curr, cmd->key1, cmd->modif1))
505 {
506 // now solve some quirks manually
507 if (curr->key == GDK_KEY_0 && !VI_IS_INSERT(mode))
508 {
509 // 0 jumps to the beginning of line only when not preceded
510 // by another number in which case we want to add it to the accumulator
511 if (prev == NULL || !kp_isdigit(prev))
512 return cmd;
513 }
514 else if (curr->key == GDK_KEY_percent && !VI_IS_INSERT(mode))
515 {
516 // % when preceded by a number jumps to N% of the file, otherwise
517 // % goes to matching brace
518 Cmd c = cmd_goto_matching_brace;
519 gint val = kpl_get_int(below, NULL);
520 if (val != -1)
521 c = cmd_goto_doc_percentage;
522 if (cmd->cmd == c)
523 return cmd;
524 }
525 else if (prev && prev->key == GDK_KEY_g && !VI_IS_INSERT(mode))
526 {
527 // takes care of operator commands like g~, gu, gU where we
528 // have no selection yet so the 2-letter command isn't found
529 // above and a corresponding 1-letter command ~, u, U exists and
530 // would be used instead of waiting for the full command
531 }
532 else if (is_cmdpart(kpl, text_object_cmds) &&
533 get_cmd_to_run(below, operator_cmds, TRUE) && !VI_IS_INSERT(mode))
534 {
535 // if we received "a" or "i", we have to check if there's not
536 // an operator command below because these can be part of
537 // text object commands (like a<) and in this case we don't
538 // want to have "a" or "i" executed yet
539 }
540 else
541 return cmd;
542 }
543 }
544
545 return NULL;
546 }
547
548
perform_cmd(CmdDef * def,CmdContext * ctx)549 static void perform_cmd(CmdDef *def, CmdContext *ctx)
550 {
551 GSList *top;
552 gint num;
553 gint cmd_len = 0;
554 gboolean num_present;
555 CmdParams param;
556 gint orig_pos = SSM(ctx->sci, SCI_GETCURRENTPOS, 0, 0);
557 gint sel_start, sel_len;
558
559 if (def->key1 != 0)
560 cmd_len++;
561 if (def->key2 != 0)
562 cmd_len++;
563 if (def->param)
564 cmd_len++;
565 top = g_slist_nth(ctx->kpl, cmd_len);
566 num = kpl_get_int(top, &top);
567 num_present = num != -1;
568
569 sel_start = SSM(ctx->sci, SCI_GETSELECTIONSTART, 0, 0);
570 sel_len = SSM(ctx->sci, SCI_GETSELECTIONEND, 0, 0) - sel_start;
571 cmd_params_init(¶m, ctx->sci,
572 num_present ? num : 1, num_present, ctx->kpl, FALSE,
573 sel_start, sel_len);
574
575 SSM(ctx->sci, SCI_BEGINUNDOACTION, 0, 0);
576
577 // if (def->cmd != cmd_undo && def->cmd != cmd_redo)
578 // SSM(sci, SCI_ADDUNDOACTION, param.pos, UNDO_MAY_COALESCE);
579
580 def->cmd(ctx, ¶m);
581
582 if (VI_IS_COMMAND(vi_get_mode()))
583 {
584 gboolean is_text_object_cmd = is_in_cmd_group(text_object_cmds, def);
585 gboolean is_include_dest_char_movement_cmd = is_in_cmd_group(include_dest_char_movement_cmds, def);
586 if (is_text_object_cmd || is_in_cmd_group(movement_cmds, def))
587 {
588 def = get_cmd_to_run(top, operator_cmds, TRUE);
589 if (def)
590 {
591 gint new_pos = SSM(ctx->sci, SCI_GETCURRENTPOS, 0, 0);
592
593 SET_POS(ctx->sci, orig_pos, FALSE);
594
595 if (is_text_object_cmd)
596 {
597 sel_start = param.sel_start;
598 sel_len = param.sel_len;
599 }
600 else
601 {
602 sel_start = MIN(new_pos, orig_pos);
603 sel_len = ABS(new_pos - orig_pos);
604 if (sel_len > 0 && is_include_dest_char_movement_cmd)
605 {
606 sel_len++;
607 if (new_pos < orig_pos)
608 sel_start--;
609 }
610 }
611 cmd_params_init(¶m, ctx->sci,
612 1, FALSE, top, TRUE,
613 sel_start, sel_len);
614
615 def->cmd(ctx, ¶m);
616 }
617 }
618 }
619
620 /* mode could have changed after performing command */
621 if (VI_IS_COMMAND(vi_get_mode()))
622 clamp_cursor_pos(ctx->sci);
623
624 SSM(ctx->sci, SCI_ENDUNDOACTION, 0, 0);
625 }
626
627
perform_repeat_cmd(CmdContext * ctx)628 static gboolean perform_repeat_cmd(CmdContext *ctx)
629 {
630 GSList *top = g_slist_next(ctx->kpl); // get behind "."
631 gint num = kpl_get_int(top, NULL);
632 CmdDef *def;
633 gint i;
634
635 def = get_cmd_to_run(ctx->repeat_kpl, edit_cmds, FALSE);
636 num = num == -1 ? 1 : num;
637 if (def) {
638 for (i = 0; i < num; i++)
639 perform_cmd(def, ctx);
640 return TRUE;
641 }
642 else if (ctx->insert_buf_len > 0) {
643 gint pos;
644
645 SSM(ctx->sci, SCI_BEGINUNDOACTION, 0, 0);
646 for (i = 0; i < num; i++)
647 SSM(ctx->sci, SCI_ADDTEXT, ctx->insert_buf_len, (sptr_t) ctx->insert_buf);
648 pos = SSM(ctx->sci, SCI_GETCURRENTPOS, 0, 0);
649 SET_POS(ctx->sci, PREV(ctx->sci, pos), FALSE);
650 SSM(ctx->sci, SCI_ENDUNDOACTION, 0, 0);
651 return TRUE;
652 }
653
654 return FALSE;
655 }
656
657
process_cmd(CmdDef * cmds,CmdContext * ctx,gboolean ins_mode)658 static gboolean process_cmd(CmdDef *cmds, CmdContext *ctx, gboolean ins_mode)
659 {
660 gboolean consumed;
661 gboolean performed = FALSE;
662 ViMode orig_mode = vi_get_mode();
663 gboolean have_selection =
664 SSM(ctx->sci, SCI_GETSELECTIONEND, 0, 0) - SSM(ctx->sci, SCI_GETSELECTIONSTART, 0, 0) > 0;
665 CmdDef *def = get_cmd_to_run(ctx->kpl, cmds, have_selection);
666
667 consumed = is_cmdpart(ctx->kpl, cmds) || (!ins_mode && is_printable(ctx->kpl));
668
669 if (def)
670 {
671 if (def->cmd == cmd_repeat_last_command)
672 {
673 if (perform_repeat_cmd(ctx))
674 {
675 performed = TRUE;
676
677 g_slist_free_full(ctx->kpl, g_free);
678 ctx->kpl = NULL;
679 }
680 }
681 else
682 {
683 perform_cmd(def, ctx);
684 performed = TRUE;
685
686 if (is_in_cmd_group(edit_cmds, def))
687 {
688 g_slist_free_full(ctx->repeat_kpl, g_free);
689 ctx->repeat_kpl = ctx->kpl;
690 }
691 else
692 g_slist_free_full(ctx->kpl, g_free);
693 ctx->kpl = NULL;
694 }
695 }
696
697 consumed = consumed || performed;
698
699 if (performed)
700 {
701 if (orig_mode == VI_MODE_COMMAND_SINGLE)
702 vi_set_mode(VI_MODE_INSERT);
703 }
704 else if (!consumed && ctx->kpl)
705 {
706 g_free(ctx->kpl->data);
707 ctx->kpl = g_slist_delete_link(ctx->kpl, ctx->kpl);
708 }
709
710 return consumed;
711 }
712
713
cmd_perform_cmd(CmdContext * ctx)714 gboolean cmd_perform_cmd(CmdContext *ctx)
715 {
716 return process_cmd(cmd_mode_cmds, ctx, FALSE);
717 }
718
719
cmd_perform_vis(CmdContext * ctx)720 gboolean cmd_perform_vis(CmdContext *ctx)
721 {
722 return process_cmd(vis_mode_cmds, ctx, FALSE);
723 }
724
725
cmd_perform_ins(CmdContext * ctx)726 gboolean cmd_perform_ins(CmdContext *ctx)
727 {
728 return process_cmd(ins_mode_cmds, ctx, TRUE);
729 }
730