keys_basic.c - ledit - Text editor (WIP) | |
git clone git://lumidify.org/ledit.git (fast, but not encrypted) | |
git clone https://lumidify.org/ledit.git (encrypted, but very slow) | |
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/… | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
keys_basic.c (104268B) | |
--- | |
1 /* FIXME: all movement commands that modify the selection should first c… | |
2 /* FIXME: should motion callbacks be ignored in visual mode as they curr… | |
3 /* FIXME: check allowed modes at beginning of functions */ | |
4 /* FIXME: the stacks here are shared for all views which can cause weird | |
5 behavior, but I'm not sure what would be more logical */ | |
6 /* FIXME: cursor isn't shown properly on spaces at end of softlines */ | |
7 /* FIXME: selection is sometimes not reset when it is "clicked away" */ | |
8 /* FIXME: use weak cursor */ | |
9 /* FIXME: spaces at end of soft line are weird in bidi text | |
10 -> space is hidden when e.g. ltr text left and rtl text on right is w… | |
11 /* FIXME: some weird things still happen with selections staying as "gho… | |
12 and being deleted at some later time even though they're not shown an… | |
13 /* FIXME: delete everything concerned with selections in insert mode sin… | |
14 they are now not allowed at all */ | |
15 /* FIXME: a lot of error checking in the individual functions may be red… | |
16 now that more checking is done beforehand for the allowed keys */ | |
17 /* FIXME: sort functions a bit better, maybe split file */ | |
18 /* FIXME: documentation */ | |
19 #include <stdio.h> | |
20 #include <ctype.h> | |
21 #include <stdlib.h> | |
22 | |
23 #include <X11/Xlib.h> | |
24 #include <X11/Xutil.h> | |
25 #include <pango/pangoxft.h> | |
26 #include <X11/extensions/Xdbe.h> | |
27 #include <X11/keysym.h> | |
28 #include <X11/XF86keysym.h> | |
29 #include <X11/cursorfont.h> | |
30 | |
31 #if ENABLE_UTF8PROC | |
32 #include "utf8proc.h" | |
33 #endif | |
34 | |
35 #include "util.h" | |
36 #include "assert.h" | |
37 #include "memory.h" | |
38 #include "common.h" | |
39 #include "txtbuf.h" | |
40 #include "undo.h" | |
41 #include "cache.h" | |
42 #include "window.h" | |
43 #include "buffer.h" | |
44 #include "view.h" | |
45 #include "search.h" | |
46 | |
47 #include "keys.h" | |
48 #include "keys_basic.h" | |
49 #include "keys_command.h" | |
50 #include "configparser.h" | |
51 | |
52 /***********************************************************************… | |
53 * Declarations for all functions that can be used in the configuration.… | |
54 ***********************************************************************… | |
55 | |
56 static struct action cursor_left(ledit_view *view, char *text, size_t le… | |
57 static struct action cursor_right(ledit_view *view, char *text, size_t l… | |
58 static struct action cursor_up(ledit_view *view, char *text, size_t len); | |
59 static struct action cursor_down(ledit_view *view, char *text, size_t le… | |
60 static struct action break_line(ledit_view *view, char *text, size_t len… | |
61 static struct action return_to_normal(ledit_view *view, char *text, size… | |
62 static struct action enter_insert(ledit_view *view, char *text, size_t l… | |
63 static struct action cursor_to_beginning(ledit_view *view, char *text, s… | |
64 static struct action key_0(ledit_view *view, char *text, size_t len); | |
65 static struct action push_0(ledit_view *view, char *text, size_t len); | |
66 static struct action push_1(ledit_view *view, char *text, size_t len); | |
67 static struct action push_2(ledit_view *view, char *text, size_t len); | |
68 static struct action push_3(ledit_view *view, char *text, size_t len); | |
69 static struct action push_4(ledit_view *view, char *text, size_t len); | |
70 static struct action push_5(ledit_view *view, char *text, size_t len); | |
71 static struct action push_6(ledit_view *view, char *text, size_t len); | |
72 static struct action push_7(ledit_view *view, char *text, size_t len); | |
73 static struct action push_8(ledit_view *view, char *text, size_t len); | |
74 static struct action push_9(ledit_view *view, char *text, size_t len); | |
75 static struct action delete(ledit_view *view, char *text, size_t len); | |
76 static struct action enter_visual(ledit_view *view, char *text, size_t l… | |
77 static struct action switch_selection_end(ledit_view *view, char *text, … | |
78 static struct action clipcopy(ledit_view *view, char *text, size_t len); | |
79 static struct action clippaste(ledit_view *view, char *text, size_t len); | |
80 static struct action show_line(ledit_view *view, char *text, size_t len); | |
81 static struct action enter_commandedit(ledit_view *view, char *text, siz… | |
82 static struct action enter_searchedit_backward(ledit_view *view, char *t… | |
83 static struct action enter_searchedit_forward(ledit_view *view, char *te… | |
84 static struct action key_search_next(ledit_view *view, char *text, size_… | |
85 static struct action key_search_prev(ledit_view *view, char *text, size_… | |
86 static struct action undo(ledit_view *view, char *text, size_t len); | |
87 static struct action redo(ledit_view *view, char *text, size_t len); | |
88 static struct action insert_mode_insert_text(ledit_view *view, char *tex… | |
89 static struct action repeat_command(ledit_view *view, char *text, size_t… | |
90 static struct action screen_up(ledit_view *view, char *text, size_t len); | |
91 static struct action screen_down(ledit_view *view, char *text, size_t le… | |
92 static struct action scroll_with_cursor_up(ledit_view *view, char *text,… | |
93 static struct action scroll_with_cursor_down(ledit_view *view, char *tex… | |
94 static struct action scroll_lines_up(ledit_view *view, char *text, size_… | |
95 static struct action scroll_lines_down(ledit_view *view, char *text, siz… | |
96 static struct action move_to_line(ledit_view *view, char *text, size_t l… | |
97 static struct action paste_normal(ledit_view *view, char *text, size_t l… | |
98 static struct action paste_normal_backwards(ledit_view *view, char *text… | |
99 static struct action change(ledit_view *view, char *text, size_t len); | |
100 static struct action move_to_eol(ledit_view *view, char *text, size_t le… | |
101 static struct action insert_mark(ledit_view *view, char *text, size_t le… | |
102 static struct action jump_to_mark(ledit_view *view, char *text, size_t l… | |
103 static struct action next_word(ledit_view *view, char *text, size_t len); | |
104 static struct action next_word_end(ledit_view *view, char *text, size_t … | |
105 static struct action next_bigword(ledit_view *view, char *text, size_t l… | |
106 static struct action next_bigword_end(ledit_view *view, char *text, size… | |
107 static struct action prev_word(ledit_view *view, char *text, size_t len); | |
108 static struct action prev_bigword(ledit_view *view, char *text, size_t l… | |
109 static struct action append_after_eol(ledit_view *view, char *text, size… | |
110 static struct action append_after_cursor(ledit_view *view, char *text, s… | |
111 static struct action append_line_above(ledit_view *view, char *text, siz… | |
112 static struct action append_line_below(ledit_view *view, char *text, siz… | |
113 static struct action find_next_char_forwards(ledit_view *view, char *tex… | |
114 static struct action find_next_char_backwards(ledit_view *view, char *te… | |
115 static struct action find_char_forwards(ledit_view *view, char *text, si… | |
116 static struct action find_char_backwards(ledit_view *view, char *text, s… | |
117 static struct action change_to_eol(ledit_view *view, char *text, size_t … | |
118 static struct action delete_to_eol(ledit_view *view, char *text, size_t … | |
119 static struct action delete_chars_forwards(ledit_view *view, char *text,… | |
120 static struct action delete_chars_backwards(ledit_view *view, char *text… | |
121 static struct action delete_chars_forwards_multiline(ledit_view *view, c… | |
122 static struct action delete_chars_backwards_multiline(ledit_view *view, … | |
123 static struct action delete_graphemes_forwards(ledit_view *view, char *t… | |
124 static struct action delete_graphemes_backwards(ledit_view *view, char *… | |
125 static struct action delete_graphemes_forwards_multiline(ledit_view *vie… | |
126 static struct action delete_graphemes_backwards_multiline(ledit_view *vi… | |
127 static struct action yank(ledit_view *view, char *text, size_t len); | |
128 static struct action yank_lines(ledit_view *view, char *text, size_t len… | |
129 static struct action uppercase(ledit_view *view, char *text, size_t len); | |
130 static struct action lowercase(ledit_view *view, char *text, size_t len); | |
131 static struct action replace(ledit_view *view, char *text, size_t len); | |
132 static struct action cursor_to_first_non_ws(ledit_view *view, char *text… | |
133 static struct action join_lines(ledit_view *view, char *text, size_t len… | |
134 static struct action insert_at_beginning(ledit_view *view, char *text, s… | |
135 static struct action toggle_hard_line_based(ledit_view *view, char *text… | |
136 | |
137 /*********************************************** | |
138 * String-function mapping for config parsing. * | |
139 ***********************************************/ | |
140 | |
141 /* FIXME: delete-backwards, delete-forwards should be renamed; | |
142 *key functions should be renamed (they're very vague) */ | |
143 | |
144 typedef enum { | |
145 KEY_FLAG_NONE = 0, | |
146 KEY_FLAG_JUMP_TO_CURSOR = 1, | |
147 KEY_FLAG_LOCK_ALLOWED = 2 | |
148 } basic_key_cb_flags; | |
149 | |
150 typedef struct action (*basic_key_cb_func)(ledit_view *, char *, size_t); | |
151 | |
152 struct basic_key_cb { | |
153 char *text; | |
154 basic_key_cb_func func; | |
155 basic_key_cb_flags flags; | |
156 ledit_mode allowed_modes; | |
157 }; | |
158 | |
159 int | |
160 basic_key_cb_modemask_is_valid(basic_key_cb *cb, ledit_mode modes) { | |
161 return (~cb->allowed_modes & modes) == 0; | |
162 } | |
163 | |
164 /* FIXME: make functions work in more modes (e.g. cursor-to-first-non-wh… | |
165 static struct basic_key_cb basic_key_cb_map[] = { | |
166 {"append-after-cursor", &append_after_cursor, KEY_FLAG_JUMP_TO_C… | |
167 {"append-after-eol", &append_after_eol, KEY_FLAG_JUMP_TO_CURSOR,… | |
168 {"append-line-above", &append_line_above, KEY_FLAG_JUMP_TO_CURSO… | |
169 {"append-line-below", &append_line_below, KEY_FLAG_JUMP_TO_CURSO… | |
170 {"break-line", &break_line, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSE… | |
171 {"change", &change, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSER… | |
172 {"change-to-eol", &change_to_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMA… | |
173 {"clipboard-copy", &clipcopy, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_L… | |
174 {"clipboard-paste", &clippaste, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|… | |
175 {"cursor-down", &cursor_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_L… | |
176 {"cursor-left", &cursor_left, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_L… | |
177 {"cursor-right", &cursor_right, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG… | |
178 {"cursor-to-beginning", &cursor_to_beginning, KEY_FLAG_JUMP_TO_C… | |
179 {"cursor-to-first-non-whitespace", &cursor_to_first_non_ws, KEY_… | |
180 {"cursor-up", &cursor_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
181 {"delete", &delete, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSER… | |
182 {"delete-chars-backwards", &delete_chars_backwards, KEY_FLAG_JUM… | |
183 {"delete-chars-backwards-multiline", &delete_chars_backwards_mul… | |
184 {"delete-chars-forwards", &delete_chars_forwards, KEY_FLAG_JUMP_… | |
185 {"delete-chars-forwards-multiline", &delete_chars_forwards_multi… | |
186 {"delete-graphemes-backwards", &delete_graphemes_backwards, KEY_… | |
187 {"delete-graphemes-backwards-multiline", &delete_graphemes_backw… | |
188 {"delete-graphemes-forwards", &delete_graphemes_forwards, KEY_FL… | |
189 {"delete-graphemes-forwards-multiline", &delete_graphemes_forwar… | |
190 {"delete-to-eol", &delete_to_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMA… | |
191 {"enter-commandedit", &enter_commandedit, KEY_FLAG_NONE|KEY_FLAG… | |
192 {"enter-insert", &enter_insert, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|… | |
193 {"enter-searchedit-backwards", &enter_searchedit_backward, KEY_F… | |
194 {"enter-searchedit-forwards", &enter_searchedit_forward, KEY_FLA… | |
195 {"enter-visual", &enter_visual, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|… | |
196 {"find-char-backwards", &find_char_backwards, KEY_FLAG_JUMP_TO_C… | |
197 {"find-char-forwards", &find_char_forwards, KEY_FLAG_JUMP_TO_CUR… | |
198 {"find-next-char-backwards", &find_next_char_backwards, KEY_FLAG… | |
199 {"find-next-char-forwards", &find_next_char_forwards, KEY_FLAG_J… | |
200 {"insert-at-beginning", &insert_at_beginning, KEY_FLAG_JUMP_TO_C… | |
201 {"insert-mark", &insert_mark, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWE… | |
202 {"insert-text", &insert_mode_insert_text, KEY_FLAG_JUMP_TO_CURSO… | |
203 {"join-lines", &join_lines, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSE… | |
204 {"jump-to-mark", &jump_to_mark, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG… | |
205 {"key-0", &key_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED,… | |
206 {"lowercase", &lowercase, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT… | |
207 {"move-to-eol", &move_to_eol, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_L… | |
208 {"move-to-line", &move_to_line, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG… | |
209 {"next-bigword", &next_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG… | |
210 {"next-bigword-end", &next_bigword_end, KEY_FLAG_JUMP_TO_CURSOR|… | |
211 {"next-word", &next_word, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
212 {"next-word-end", &next_word_end, KEY_FLAG_JUMP_TO_CURSOR|KEY_FL… | |
213 {"paste-buffer", &paste_normal, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|… | |
214 {"paste-buffer-backwards", &paste_normal_backwards, KEY_FLAG_JUM… | |
215 {"previous-bigword", &prev_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_… | |
216 {"previous-word", &prev_word, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_L… | |
217 {"push-0", &push_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
218 {"push-1", &push_1, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
219 {"push-2", &push_2, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
220 {"push-3", &push_3, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
221 {"push-4", &push_4, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
222 {"push-5", &push_5, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
223 {"push-6", &push_6, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
224 {"push-7", &push_7, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
225 {"push-8", &push_8, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
226 {"push-9", &push_9, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
227 {"redo", &redo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, | |
228 {"repeat-command", &repeat_command, KEY_FLAG_JUMP_TO_CURSOR, NOR… | |
229 {"replace", &replace, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, | |
230 {"return-to-normal", &return_to_normal, KEY_FLAG_JUMP_TO_CURSOR|… | |
231 {"screen-down", &screen_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_L… | |
232 {"screen-up", &screen_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
233 {"scroll-lines-down", &scroll_lines_down, KEY_FLAG_JUMP_TO_CURSO… | |
234 {"scroll-lines-up", &scroll_lines_up, KEY_FLAG_JUMP_TO_CURSOR|KE… | |
235 {"scroll-with-cursor-down", &scroll_with_cursor_down, KEY_FLAG_J… | |
236 {"scroll-with-cursor-up", &scroll_with_cursor_up, KEY_FLAG_JUMP_… | |
237 {"search-next", &key_search_next, KEY_FLAG_JUMP_TO_CURSOR|KEY_FL… | |
238 {"search-previous", &key_search_prev, KEY_FLAG_JUMP_TO_CURSOR|KE… | |
239 {"show-line", &show_line, KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|I… | |
240 {"switch-selection-end", &switch_selection_end, KEY_FLAG_JUMP_TO… | |
241 {"toggle-hard-line-based", &toggle_hard_line_based, KEY_FLAG_NON… | |
242 {"undo", &undo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, | |
243 {"uppercase", &uppercase, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT… | |
244 {"yank", &yank, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, N… | |
245 {"yank-lines", &yank_lines, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOC… | |
246 }; | |
247 | |
248 GEN_CB_MAP_HELPERS(basic_key_cb_map, basic_key_cb, text) | |
249 | |
250 /*************************************************** | |
251 * General global variables and utility functions. * | |
252 ***************************************************/ | |
253 | |
254 enum key_type { | |
255 KEY_INVALID = 0, | |
256 KEY_MOTION_CHAR = 4, | |
257 KEY_MOTION_LINE = 8, | |
258 KEY_MOTION = 4|8, | |
259 KEY_MOTIONALLOWED = 16, | |
260 KEY_NUMBER = 32, | |
261 KEY_NUMBERALLOWED = 64, | |
262 KEY_ANY = 0xFF | |
263 }; | |
264 | |
265 /* note: this is supposed to be global for all views/buffers */ | |
266 static int paste_buffer_line_based = 0; | |
267 static txtbuf *paste_buffer = NULL; | |
268 static int last_lines_scrolled = -1; | |
269 | |
270 struct repetition_stack_elem { | |
271 char *key_text; | |
272 size_t len; | |
273 KeySym sym; | |
274 unsigned int key_state; | |
275 int lang_index; | |
276 }; | |
277 | |
278 static struct { | |
279 int replaying; | |
280 size_t len, alloc, cur_pos; | |
281 struct repetition_stack_elem *stack; | |
282 size_t tmp_len, tmp_alloc; | |
283 struct repetition_stack_elem *tmp_stack; | |
284 } repetition_stack = {0, 0, 0, 0, NULL, 0, 0, NULL}; | |
285 | |
286 typedef void (*motion_callback)(ledit_view *view, size_t line, size_t ch… | |
287 | |
288 struct key_stack_elem { | |
289 enum key_type key; | |
290 enum key_type followup; /* allowed keys to complete the keybindi… | |
291 /* callback function that motion commands call to complete a com… | |
292 * line and char_pos already include the repetition stored in th… | |
293 * element; type is the type of motion command (used to determin… | |
294 * the command should operate on lines or chars) */ | |
295 motion_callback motion_cb; | |
296 int count; /* number of repetitions */ | |
297 }; | |
298 | |
299 static struct { | |
300 size_t len, alloc; | |
301 struct key_stack_elem *stack; | |
302 } key_stack = {0, 0, NULL}; | |
303 | |
304 static struct action (*grab_char_cb)(ledit_view *view, char *text, size_… | |
305 | |
306 void | |
307 basic_key_cleanup(void) { | |
308 /* this should be safe since push_repetition_stack sets all new | |
309 elements to NULL when resizing the stack */ | |
310 for (size_t i = 0; i < repetition_stack.alloc; i++) { | |
311 free(repetition_stack.stack[i].key_text); | |
312 } | |
313 for (size_t i = 0; i < repetition_stack.tmp_alloc; i++) { | |
314 free(repetition_stack.tmp_stack[i].key_text); | |
315 } | |
316 free(repetition_stack.stack); | |
317 free(repetition_stack.tmp_stack); | |
318 free(key_stack.stack); | |
319 } | |
320 | |
321 /* No, this isn't actually a stack. So what? */ | |
322 static struct repetition_stack_elem *push_repetition_stack(void); | |
323 static void finalize_repetition_stack(void); | |
324 static struct repetition_stack_elem *get_cur_repetition_stack_elem(void); | |
325 static void unwind_repetition_stack(void); | |
326 static void advance_repetition_stack(void); | |
327 static void discard_repetition_stack(void); | |
328 | |
329 static int key_stack_empty(void); | |
330 static struct key_stack_elem *push_key_stack(void); | |
331 static struct key_stack_elem *peek_key_stack(void); | |
332 static struct key_stack_elem *pop_key_stack(void); | |
333 void clear_key_stack(void); | |
334 | |
335 static void move_cursor_left_right(ledit_view *view, int dir, int allow_… | |
336 static void move_cursor_up_down(ledit_view *view, int dir); | |
337 static void push_num(ledit_view *view, int num); | |
338 static void delete_cb(ledit_view *view, size_t line, size_t char_pos, en… | |
339 static void yank_cb(ledit_view *view, size_t line, size_t char_pos, enum… | |
340 static void get_new_line_softline( | |
341 ledit_view *view, size_t cur_line, size_t cur_index, | |
342 int movement, size_t *new_line_ret, int *new_softline_ret | |
343 ); | |
344 static void move_cursor_logically(ledit_view *view, int movement_dir, in… | |
345 static void change_cb(ledit_view *view, size_t line, size_t char_pos, en… | |
346 static void push_undo_empty_insert(ledit_view *view, size_t line, size_t… | |
347 static void move_half_screen(ledit_view *view, int movement); | |
348 | |
349 static struct action | |
350 view_locked_error(ledit_view *view) { | |
351 window_show_message(view->window, view->lock_text, -1); | |
352 return (struct action){ACTION_NONE, NULL}; | |
353 } | |
354 | |
355 #define CHECK_VIEW_LOCKED(view) if (view->lock_text) return view_locked_… | |
356 #define CHECK_VIEW_LOCKED_NORETURN(view) if (view->lock_text) (void)view… | |
357 | |
358 static int | |
359 key_stack_empty(void) { | |
360 return key_stack.len == 0; | |
361 } | |
362 | |
363 static struct key_stack_elem * | |
364 push_key_stack(void) { | |
365 struct key_stack_elem *e; | |
366 if (key_stack.len >= key_stack.alloc) { | |
367 size_t new_alloc = ideal_array_size(key_stack.alloc, add… | |
368 key_stack.stack = ledit_reallocarray( | |
369 key_stack.stack, new_alloc, sizeof(struct key_stack_… | |
370 ); | |
371 key_stack.alloc = new_alloc; | |
372 } | |
373 e = &key_stack.stack[key_stack.len]; | |
374 e->key = KEY_INVALID; | |
375 e->followup = KEY_INVALID; | |
376 e->motion_cb = NULL; | |
377 e->count = 0; | |
378 key_stack.len++; | |
379 return e; | |
380 } | |
381 | |
382 /* Note: for peek and pop, the returned element is only valid | |
383 * until the next element is pushed */ | |
384 /* Note on the note: that's not entirely true for peek */ | |
385 static struct key_stack_elem * | |
386 peek_key_stack(void) { | |
387 if (key_stack.len > 0) | |
388 return &key_stack.stack[key_stack.len - 1]; | |
389 return NULL; | |
390 } | |
391 | |
392 static struct key_stack_elem * | |
393 pop_key_stack(void) { | |
394 if (key_stack.len > 0) { | |
395 key_stack.len--; | |
396 return &key_stack.stack[key_stack.len]; | |
397 } | |
398 return NULL; | |
399 } | |
400 | |
401 void | |
402 clear_key_stack(void) { | |
403 key_stack.len = 0; | |
404 } | |
405 | |
406 static struct action | |
407 err_invalid_key(ledit_view *view) { | |
408 window_show_message(view->window, "Invalid key", -1); | |
409 clear_key_stack(); | |
410 discard_repetition_stack(); | |
411 return (struct action){ACTION_NONE, NULL}; | |
412 } | |
413 | |
414 /* | |
415 * Get the number of times a command should be repeated and the motion | |
416 * callback, if there was one on the stack. | |
417 * Note that *cb_ret is set to NULL if a non-null address is given and | |
418 * there is no motion callback on the stack. | |
419 * An empty stack or a stack where the top element has a count of 0 | |
420 * leads to 0 being returned. | |
421 * In case of error, -1 is returned: | |
422 * - When the top or second element is not a number key but also does | |
423 * not comtain a motion callback. | |
424 * - When the stack is not empty after removing the top element and | |
425 * possibly a second one if the top one was a number key. | |
426 */ | |
427 static int | |
428 get_key_repeat_and_motion_cb(ledit_view *view, motion_callback *cb_ret) { | |
429 int num = 1; | |
430 struct key_stack_elem *e = pop_key_stack(); | |
431 if (e != NULL) { | |
432 if (e->key & KEY_NUMBER) { | |
433 num = e->count; | |
434 e = pop_key_stack(); | |
435 } else if (e->count == 0) { | |
436 num = 0; | |
437 } | |
438 if (e != NULL) { | |
439 int new_count = e->count > 0 ? e->count : 1; | |
440 if (INT_MAX / new_count < num) { | |
441 window_show_message( | |
442 view->window, | |
443 "Integer overflow in key repetition"… | |
444 ); | |
445 num = INT_MAX; | |
446 } | |
447 num *= new_count; | |
448 } | |
449 } else { | |
450 num = 0; | |
451 } | |
452 if (e != NULL && !(e->key & KEY_NUMBER) && e->motion_cb == NULL) | |
453 num = -1; | |
454 else if (!key_stack_empty()) | |
455 num = -1; | |
456 if (cb_ret != NULL && e != NULL && e->motion_cb != NULL) | |
457 *cb_ret = e->motion_cb; | |
458 else if (cb_ret != NULL) | |
459 *cb_ret = NULL; | |
460 return num; | |
461 } | |
462 | |
463 /* | |
464 * Get the number of times a command should be repeated, or -1 if anythi… | |
465 * invalid was on the stack - this is for commands that just take a repe… | |
466 * count and nothing else (cursor movement keys are different because th… | |
467 * can use other elements on the key stack too, for instance call a call… | |
468 * as is done for deletion. | |
469 * Note that an empty stack leads to 0 being returned even though most c… | |
470 * use 1 as repeat then so the caller can distinguish between empty stac… | |
471 * a repetition count of 1. | |
472 */ | |
473 static int | |
474 get_key_repeat(void) { | |
475 int num = 1; | |
476 struct key_stack_elem *e = pop_key_stack(); | |
477 if (e != NULL) { | |
478 if (e->key & KEY_NUMBER) { | |
479 num = e->count > 0 ? e->count : 1; | |
480 e = pop_key_stack(); | |
481 } | |
482 if (e != NULL) { | |
483 /* the key was not a number, or there was another | |
484 element under it on the stack -> error */ | |
485 num = -1; | |
486 } | |
487 } else { | |
488 num = 0; | |
489 } | |
490 clear_key_stack(); | |
491 return num; | |
492 } | |
493 | |
494 static struct repetition_stack_elem * | |
495 push_repetition_stack(void) { | |
496 struct repetition_stack_elem *e; | |
497 if (repetition_stack.tmp_len >= repetition_stack.tmp_alloc) { | |
498 size_t new_alloc = ideal_array_size(repetition_stack.tmp… | |
499 repetition_stack.tmp_stack = ledit_reallocarray( | |
500 repetition_stack.tmp_stack, | |
501 new_alloc, sizeof(struct repetition_stack_elem) | |
502 ); | |
503 for (size_t i = repetition_stack.tmp_alloc; i < new_allo… | |
504 repetition_stack.tmp_stack[i].key_text = NULL; | |
505 } | |
506 repetition_stack.tmp_alloc = new_alloc; | |
507 } | |
508 e = &repetition_stack.tmp_stack[repetition_stack.tmp_len]; | |
509 e->key_text = NULL; | |
510 e->len = 0; | |
511 e->sym = 0; | |
512 e->key_state = 0; | |
513 e->lang_index = 0; | |
514 repetition_stack.tmp_len++; | |
515 return e; | |
516 } | |
517 | |
518 static struct repetition_stack_elem * | |
519 get_cur_repetition_stack_elem(void) { | |
520 if (repetition_stack.cur_pos >= repetition_stack.len) | |
521 return NULL; | |
522 return &repetition_stack.stack[repetition_stack.cur_pos]; | |
523 } | |
524 | |
525 static void | |
526 unwind_repetition_stack(void) { | |
527 repetition_stack.cur_pos = 0; | |
528 } | |
529 | |
530 static void | |
531 discard_repetition_stack(void) { | |
532 if (repetition_stack.replaying) | |
533 return; | |
534 for (size_t i = 0; i < repetition_stack.tmp_len; i++) { | |
535 free(repetition_stack.tmp_stack[i].key_text); | |
536 repetition_stack.tmp_stack[i].key_text = NULL; | |
537 } | |
538 repetition_stack.tmp_len = 0; | |
539 } | |
540 | |
541 static void | |
542 advance_repetition_stack(void) { | |
543 repetition_stack.cur_pos++; | |
544 } | |
545 | |
546 static void | |
547 finalize_repetition_stack(void) { | |
548 if (repetition_stack.replaying) | |
549 return; | |
550 size_t tmp; | |
551 for (size_t i = 0; i < repetition_stack.len; i++) { | |
552 free(repetition_stack.stack[i].key_text); | |
553 repetition_stack.stack[i].key_text = NULL; | |
554 } | |
555 struct repetition_stack_elem *tmpstack; | |
556 repetition_stack.len = repetition_stack.tmp_len; | |
557 repetition_stack.tmp_len = 0; | |
558 tmp = repetition_stack.alloc; | |
559 repetition_stack.alloc = repetition_stack.tmp_alloc; | |
560 repetition_stack.tmp_alloc = tmp; | |
561 tmpstack = repetition_stack.stack; | |
562 repetition_stack.stack = repetition_stack.tmp_stack; | |
563 repetition_stack.tmp_stack = tmpstack; | |
564 } | |
565 | |
566 /* get the new line and softline when moving 'movement' softlines | |
567 (or hardlines if hard_line_based is set) up or | |
568 down (negative means up, positive means down) */ | |
569 static void | |
570 get_new_line_softline( | |
571 ledit_view *view, size_t cur_line, size_t cur_index, int movement, | |
572 size_t *new_line_ret, int *new_softline_ret) { | |
573 if (view->buffer->hard_line_based) { | |
574 if (movement < 0 && (size_t)-movement > cur_line) | |
575 *new_line_ret = 0; | |
576 else | |
577 *new_line_ret = cur_line + movement; | |
578 if (*new_line_ret >= view->lines_num) | |
579 *new_line_ret = view->lines_num - 1; | |
580 *new_softline_ret = 0; | |
581 } else { | |
582 int softline = view_pos_to_softline(view, cur_line, cur_… | |
583 if (movement > 0) { | |
584 int softlines = view_get_softline_count(view, cu… | |
585 if (softlines - softline > movement) { | |
586 *new_line_ret = cur_line; | |
587 *new_softline_ret = softline + movement; | |
588 } else { | |
589 movement -= (softlines - softline - 1); | |
590 size_t endline = cur_line + 1; | |
591 while (movement > 0 && endline < view->l… | |
592 softlines = view_get_softline_co… | |
593 movement -= softlines; | |
594 endline++; | |
595 } | |
596 endline--; | |
597 if (movement <= 0) { | |
598 *new_softline_ret = movement + s… | |
599 } else { | |
600 *new_softline_ret = softlines - … | |
601 } | |
602 *new_line_ret = endline; | |
603 } | |
604 } else if (movement < 0) { | |
605 int softlines = 0; | |
606 if (softline + movement >= 0) { | |
607 *new_line_ret = cur_line; | |
608 *new_softline_ret = softline + movement; | |
609 } else { | |
610 movement += softline; | |
611 size_t endline = cur_line; | |
612 while (movement < 0 && endline > 0) { | |
613 softlines = view_get_softline_co… | |
614 movement += softlines; | |
615 endline--; | |
616 } | |
617 if (movement >= 0) { | |
618 *new_softline_ret = movement; | |
619 } else { | |
620 *new_softline_ret = 0; | |
621 } | |
622 *new_line_ret = endline; | |
623 } | |
624 } else { | |
625 *new_line_ret = cur_line; | |
626 *new_softline_ret = softline; | |
627 } | |
628 } | |
629 } | |
630 | |
631 /* FIXME: don't overwrite view->cur_line, etc. here? */ | |
632 static void | |
633 delete_range( | |
634 ledit_view *view, | |
635 int line_based, int selected, | |
636 size_t line_index1, size_t byte_index1, | |
637 size_t line_index2, size_t byte_index2, | |
638 int copy_to_buffer) { | |
639 (void)selected; /* FIXME */ | |
640 if (copy_to_buffer && !paste_buffer) | |
641 paste_buffer = txtbuf_new(); | |
642 txtbuf *buf = copy_to_buffer ? paste_buffer : NULL; | |
643 enum delete_mode delmode = DELETE_CHAR; | |
644 if (line_based) { | |
645 if (view->buffer->hard_line_based) | |
646 delmode = DELETE_HARDLINE; | |
647 else | |
648 delmode = DELETE_SOFTLINE; | |
649 } | |
650 view_delete_range( | |
651 view, delmode, 1, | |
652 line_index1, byte_index1, | |
653 line_index2, byte_index2, | |
654 &view->cur_line, &view->cur_index, | |
655 buf | |
656 ); | |
657 } | |
658 | |
659 /* FIXME: better interface for this; documentation */ | |
660 static void | |
661 insert_text( | |
662 ledit_view *view, | |
663 size_t line, size_t index, | |
664 char *text, size_t len, | |
665 size_t cur_line1, size_t cur_index1, | |
666 size_t cur_line2, size_t cur_index2, | |
667 int set_range_start, int set_range_end, int start_group) { | |
668 ledit_range cur_range; | |
669 if (set_range_start) { | |
670 cur_range.line1 = cur_line1; | |
671 cur_range.byte1 = cur_index1; | |
672 } else { | |
673 cur_range.line1 = view->cur_line; | |
674 cur_range.byte1 = view->cur_index; | |
675 } | |
676 /* this is mainly for pasting, where the new line and index | |
677 should not be at the end of the pasted text */ | |
678 if (set_range_end) { | |
679 cur_range.line2 = cur_line2; | |
680 cur_range.byte2 = cur_index2; | |
681 } else { | |
682 /* to make static analysis happy */ | |
683 cur_range.line2 = cur_range.byte2 = 0; | |
684 } | |
685 /* FIXME: why did I ever decide to make set_range_end | |
686 mean exactly the opposite for the two functions? */ | |
687 buffer_insert_with_undo( | |
688 view->buffer, cur_range, !set_range_end, | |
689 start_group, view->mode, | |
690 line, index, text, len, | |
691 &view->cur_line, &view->cur_index | |
692 ); | |
693 if (set_range_end) { | |
694 view->cur_line = cur_line2; | |
695 view->cur_index = cur_index2; | |
696 } | |
697 } | |
698 | |
699 static int | |
700 delete_selection(ledit_view *view) { | |
701 if (view->sel_valid && (view->sel.line1 != view->sel.line2 || vi… | |
702 delete_range( | |
703 view, 0, 0, | |
704 view->sel.line1, view->sel.byte1, | |
705 view->sel.line2, view->sel.byte2, 1 | |
706 ); | |
707 paste_buffer_line_based = 0; | |
708 view->sel_valid = 0; | |
709 view->sel.line1 = view->sel.line2 = view->cur_line; | |
710 view->sel.byte1 = view->sel.byte2 = view->cur_index; | |
711 view_wipe_line_cursor_attrs(view, view->cur_line); | |
712 return 1; | |
713 } | |
714 return 0; | |
715 } | |
716 | |
717 /******************************************** | |
718 * Functions that were declared at the top. * | |
719 ********************************************/ | |
720 | |
721 /* used to set cursor - I guess this is sort of a hack */ | |
722 static void | |
723 push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int … | |
724 /* WARNING: Don't abuse txtbuf like this unless you're stupid li… | |
725 txtbuf ins_buf = {.text = "", .len = 0, .cap = 0}; | |
726 ledit_range range = {.line1 = line, .byte1 = index, .line2 = lin… | |
727 undo_push_insert( | |
728 view->buffer->undo, &ins_buf, range, range, start_group, vie… | |
729 ); | |
730 } | |
731 | |
732 static struct action | |
733 append_line_above(ledit_view *view, char *text, size_t len) { | |
734 size_t start, end; | |
735 /* do this here already so the mode group is the same for the ne… | |
736 enter_insert(view, text, len); | |
737 view_get_pos_softline_bounds(view, view->cur_line, view->cur_ind… | |
738 if (view->buffer->hard_line_based || start == 0) { | |
739 insert_text(view, view->cur_line, 0, "\n", 1, 0, 0, view… | |
740 } else { | |
741 /* FIXME: this interface really is horrible */ | |
742 insert_text(view, view->cur_line, start, "\n\n", 2, 0, 0… | |
743 } | |
744 return (struct action){ACTION_NONE, NULL}; | |
745 } | |
746 | |
747 static struct action | |
748 append_line_below(ledit_view *view, char *text, size_t len) { | |
749 size_t start, end; | |
750 enter_insert(view, text, len); | |
751 view_get_pos_softline_bounds(view, view->cur_line, view->cur_ind… | |
752 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
753 if (view->buffer->hard_line_based || end == ll->len) { | |
754 insert_text(view, view->cur_line, ll->len, "\n", 1, 0, 0… | |
755 } else { | |
756 insert_text(view, view->cur_line, end, "\n\n", 2, 0, 0, … | |
757 } | |
758 return (struct action){ACTION_NONE, NULL}; | |
759 } | |
760 | |
761 static struct action | |
762 append_after_cursor(ledit_view *view, char *text, size_t len) { | |
763 enter_insert(view, text, len); | |
764 /* make cursor jump back to original position on undo */ | |
765 push_undo_empty_insert(view, view->cur_line, view->cur_index, 1); | |
766 view_next_cursor_pos( | |
767 view, view->cur_line, view->cur_index, 1, 0, NULL, &view->cu… | |
768 ); | |
769 return (struct action){ACTION_NONE, NULL}; | |
770 } | |
771 | |
772 static struct action | |
773 append_after_eol(ledit_view *view, char *text, size_t len) { | |
774 size_t start, end; | |
775 enter_insert(view, text, len); | |
776 view_get_pos_softline_bounds(view, view->cur_line, view->cur_ind… | |
777 /* make cursor jump back to original position on undo */ | |
778 push_undo_empty_insert(view, view->cur_line, view->cur_index, 1); | |
779 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
780 if (view->buffer->hard_line_based) | |
781 view->cur_index = ll->len; | |
782 else | |
783 view->cur_index = end; | |
784 return (struct action){ACTION_NONE, NULL}; | |
785 } | |
786 | |
787 static struct action | |
788 move_to_line(ledit_view *view, char *text, size_t len) { | |
789 (void)text; | |
790 (void)len; | |
791 motion_callback cb = NULL; | |
792 int repeat = get_key_repeat_and_motion_cb(view, &cb); | |
793 size_t line; | |
794 if (repeat > 0) | |
795 line = (size_t)repeat > view->lines_num ? view->lines_nu… | |
796 else if (repeat == 0) | |
797 line = view->lines_num; | |
798 else | |
799 return err_invalid_key(view); | |
800 if (cb != NULL) { | |
801 /* this is a bit of a hack - because move_to_line always… | |
802 with hard lines, it sets the index to ll->len so e.g.… | |
803 callback deletes until the end of the line even in so… | |
804 ledit_line *ll = buffer_get_line(view->buffer, line - 1); | |
805 cb(view, line - 1, ll->len, KEY_MOTION_LINE); | |
806 } else { | |
807 if (view->mode == NORMAL) | |
808 view_wipe_line_cursor_attrs(view, view->cur_line… | |
809 else if (view->mode == VISUAL) | |
810 view_set_selection(view, view->sel.line1, view->… | |
811 view->cur_line = line - 1; | |
812 view->cur_index = 0; | |
813 int text_w, text_h; | |
814 window_get_textview_size(view->window, &text_w, &text_h); | |
815 ledit_view_line *vl = view_get_line(view, view->cur_line… | |
816 int x, y, h; | |
817 view_get_cursor_pixel_pos(view, view->cur_line, 0, &x, &… | |
818 /* if cursor is not on screen anymore, move to middle of… | |
819 if (vl->y_offset < view->display_offset || | |
820 vl->y_offset + h > view->display_offset + text_h) { | |
821 view_scroll(view, vl->y_offset - text_h / 2); | |
822 } | |
823 if (view->mode == NORMAL) | |
824 view_set_line_cursor_attrs(view, view->cur_line,… | |
825 discard_repetition_stack(); | |
826 } | |
827 return (struct action){ACTION_NONE, NULL}; | |
828 } | |
829 | |
830 /* FIXME: should these scrolling functions change behavior when hard_lin… | |
831 static void | |
832 scroll_lines(ledit_view *view, int lines, int dir) { | |
833 if (last_lines_scrolled <= 0 && lines <= 0) { | |
834 /* no scroll command yet - scroll half of screen */ | |
835 move_half_screen(view, dir); | |
836 } else { | |
837 int x, y, h, sli; | |
838 int final_lines, text_w, text_h; | |
839 ledit_view_line *vl = view_get_line(view, view->cur_line… | |
840 view_get_cursor_pixel_pos(view, view->cur_line, view->cu… | |
841 /* get the middle position of char */ | |
842 view_pos_to_x_softline(view, view->cur_line, view->cur_i… | |
843 long abs_pos = vl->y_offset + y; | |
844 window_get_textview_size(view->window, &text_w, &text_h); | |
845 if (lines > 0) | |
846 final_lines = last_lines_scrolled = lines; | |
847 else | |
848 final_lines = last_lines_scrolled; | |
849 view_wipe_line_cursor_attrs(view, view->cur_line); | |
850 get_new_line_softline( | |
851 view, view->cur_line, view->cur_index, | |
852 dir < 0 ? -final_lines : final_lines, | |
853 &view->cur_line, &sli | |
854 ); | |
855 size_t start, end; | |
856 view_get_softline_bounds(view, view->cur_line, sli, &sta… | |
857 vl = view_get_line(view, view->cur_line); | |
858 view->cur_index = view_x_softline_to_pos(view, view->cur… | |
859 view_get_cursor_pixel_pos(view, view->cur_line, view->cu… | |
860 long new_abs_pos = vl->y_offset + y; | |
861 view_scroll(view, view->display_offset + (new_abs_pos - … | |
862 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
863 } | |
864 } | |
865 | |
866 static struct action | |
867 scroll_lines_up(ledit_view *view, char *text, size_t len) { | |
868 (void)text; | |
869 (void)len; | |
870 int repeat = get_key_repeat(); | |
871 if (repeat >= 0) | |
872 scroll_lines(view, repeat, -1); | |
873 else | |
874 window_show_message(view->window, "Invalid key", -1); | |
875 discard_repetition_stack(); | |
876 return (struct action){ACTION_NONE, NULL}; | |
877 } | |
878 | |
879 static struct action | |
880 scroll_lines_down(ledit_view *view, char *text, size_t len) { | |
881 (void)text; | |
882 (void)len; | |
883 int repeat = get_key_repeat(); | |
884 if (repeat >= 0) | |
885 scroll_lines(view, repeat, 1); | |
886 else | |
887 window_show_message(view->window, "Invalid key", -1); | |
888 discard_repetition_stack(); | |
889 return (struct action){ACTION_NONE, NULL}; | |
890 } | |
891 | |
892 static void | |
893 scroll_with_cursor(ledit_view *view, int movement) { | |
894 int x, y, h; | |
895 view_get_cursor_pixel_pos(view, view->cur_line, view->cur_index,… | |
896 int pix_movement = movement * h; /* FIXME: overflow */ | |
897 view_scroll(view, view->display_offset + pix_movement); | |
898 size_t old_line = view->cur_line; | |
899 size_t old_index = view->cur_index; | |
900 view_get_nearest_legal_pos( | |
901 view, old_line, old_index, | |
902 &view->cur_line, &view->cur_index | |
903 ); | |
904 if (old_line != view->cur_line || old_index != view->cur_index) { | |
905 view_wipe_line_cursor_attrs(view, old_line); | |
906 /* if cursor is at top or bottom of screen, snap it to t… | |
907 very edge to avoid it looking weird */ | |
908 if (movement > 0) { | |
909 view_scroll_to_pos_top( | |
910 view, view->cur_line, view->cur_index | |
911 ); | |
912 } else { | |
913 view_scroll_to_pos_bottom( | |
914 view, view->cur_line, view->cur_index | |
915 ); | |
916 } | |
917 } | |
918 if (view->mode == NORMAL) { | |
919 view->cur_index = view_get_legal_normal_pos(view, view->… | |
920 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
921 } | |
922 } | |
923 | |
924 static struct action | |
925 scroll_with_cursor_up(ledit_view *view, char *text, size_t len) { | |
926 (void)text; | |
927 (void)len; | |
928 int repeat = get_key_repeat(); | |
929 if (repeat >= 0) | |
930 scroll_with_cursor(view, -(repeat == 0 ? 1 : repeat)); | |
931 else | |
932 window_show_message(view->window, "Invalid key", -1); | |
933 discard_repetition_stack(); | |
934 return (struct action){ACTION_NONE, NULL}; | |
935 } | |
936 | |
937 static struct action | |
938 scroll_with_cursor_down(ledit_view *view, char *text, size_t len) { | |
939 (void)text; | |
940 (void)len; | |
941 int repeat = get_key_repeat(); | |
942 if (repeat >= 0) | |
943 scroll_with_cursor(view, repeat == 0 ? 1 : repeat); | |
944 else | |
945 window_show_message(view->window, "Invalid key", -1); | |
946 discard_repetition_stack(); | |
947 return (struct action){ACTION_NONE, NULL}; | |
948 } | |
949 | |
950 /* FIXME: Make all these scrolling functions work in visual mode */ | |
951 /* movement is multiplied with half the window height and the result is … | |
952 the cursor is moved to the bottom if movement is upwards, to the top … | |
953 FIXME: this is slightly different now | |
954 (unless the screen is already at the very top or bottom - then it is … | |
955 /* FIXME: this is a bit weird at the moment */ | |
956 static void | |
957 move_half_screen(ledit_view *view, int movement) { | |
958 int w, h; | |
959 window_get_textview_size(view->window, &w, &h); | |
960 /* FIXME: overflow */ | |
961 int total = movement * h/2; | |
962 ledit_view_line *vl = view_get_line(view, view->cur_line); | |
963 int cur_x, cur_y, cur_h; | |
964 view_get_cursor_pixel_pos( | |
965 view, view->cur_line, view->cur_index, &cur_x, &cur_y, &cur_h | |
966 ); | |
967 long real_cur_y = vl->y_offset - view->display_offset + cur_y; | |
968 /* new pixel position of cursor */ | |
969 /* Note: this usually causes at least part of a line of overlap | |
970 because ensure_cursor_shown scrolls back a bit if the line | |
971 isn't completely shown (this behavior could be changed using | |
972 view_get_nearest_legal_pos) */ | |
973 int y = movement > 0 ? 0 : h; | |
974 int half_screen = abs(movement) % 2 == 1; | |
975 if (half_screen) { | |
976 /* if only half screens are moved and we are at the begi… | |
977 end, just move the cursor the movement amount instead… | |
978 moving it to the very top or bottom */ | |
979 if (view->display_offset + total <= 0 || | |
980 view->display_offset + total + h >= view->total_heig… | |
981 y = real_cur_y + total; | |
982 } | |
983 } else { | |
984 if (view->display_offset + total <= 0) | |
985 y = 0; | |
986 else if (view->display_offset + total + h > view->total_… | |
987 y = h; | |
988 } | |
989 if (y < 0) | |
990 y = 0; | |
991 if (y > h) | |
992 y = h; | |
993 view_scroll(view, view->display_offset + total); | |
994 view_wipe_line_cursor_attrs(view, view->cur_line); | |
995 /* try to keep current x position of cursor */ | |
996 int x, softline; | |
997 /* FIXME: properly document what uses PANGO_SCALE and what not */ | |
998 view_pos_to_x_softline(view, view->cur_line, view->cur_index, &x… | |
999 view_xy_to_line_byte( | |
1000 view, x / PANGO_SCALE, y, 0, | |
1001 &view->cur_line, &view->cur_index | |
1002 ); | |
1003 if (view->mode == NORMAL) { | |
1004 view->cur_index = view_get_legal_normal_pos(view, view->… | |
1005 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1006 } | |
1007 } | |
1008 | |
1009 static struct action | |
1010 screen_up(ledit_view *view, char *text, size_t len) { | |
1011 (void)text; | |
1012 (void)len; | |
1013 int repeat = get_key_repeat(); | |
1014 /* FIXME: overflow */ | |
1015 if (repeat >= 0) | |
1016 move_half_screen(view, -(repeat == 0 ? 2 : repeat*2)); | |
1017 else | |
1018 window_show_message(view->window, "Invalid key", -1); | |
1019 discard_repetition_stack(); | |
1020 return (struct action){ACTION_NONE, NULL}; | |
1021 } | |
1022 | |
1023 static struct action | |
1024 screen_down(ledit_view *view, char *text, size_t len) { | |
1025 (void)text; | |
1026 (void)len; | |
1027 int repeat = get_key_repeat(); | |
1028 if (repeat >= 0) | |
1029 move_half_screen(view, repeat == 0 ? 2 : repeat*2); | |
1030 else | |
1031 window_show_message(view->window, "Invalid key", -1); | |
1032 discard_repetition_stack(); | |
1033 return (struct action){ACTION_NONE, NULL}; | |
1034 } | |
1035 | |
1036 static struct action | |
1037 delete_to_eol(ledit_view *view, char *text, size_t len) { | |
1038 (void)text; | |
1039 (void)len; | |
1040 if (!key_stack_empty()) | |
1041 return err_invalid_key(view); | |
1042 size_t start, end; | |
1043 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
1044 if (view->buffer->hard_line_based) { | |
1045 end = ll->len; | |
1046 } else { | |
1047 view_get_pos_softline_bounds(view, view->cur_line, view-… | |
1048 } | |
1049 delete_range( | |
1050 view, 0, 0, | |
1051 view->cur_line, view->cur_index, | |
1052 view->cur_line, end, view->mode != INSERT | |
1053 ); | |
1054 if (view->mode != INSERT) | |
1055 paste_buffer_line_based = 0; | |
1056 if (view->mode == NORMAL) { | |
1057 view->cur_index = view_get_legal_normal_pos( | |
1058 view, view->cur_line, view->cur_index | |
1059 ); | |
1060 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1061 } | |
1062 return (struct action){ACTION_NONE, NULL}; | |
1063 } | |
1064 | |
1065 static struct action | |
1066 change_to_eol(ledit_view *view, char *text, size_t len) { | |
1067 (void)text; | |
1068 (void)len; | |
1069 if (!key_stack_empty()) | |
1070 return err_invalid_key(view); | |
1071 view_set_mode(view, INSERT); | |
1072 size_t start, end; | |
1073 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
1074 if (view->buffer->hard_line_based) { | |
1075 end = ll->len; | |
1076 } else { | |
1077 view_get_pos_softline_bounds(view, view->cur_line, view-… | |
1078 } | |
1079 delete_range( | |
1080 view, 0, 0, | |
1081 view->cur_line, view->cur_index, | |
1082 view->cur_line, end, 1 | |
1083 ); | |
1084 paste_buffer_line_based = 0; | |
1085 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1086 return (struct action){ACTION_NONE, NULL}; | |
1087 } | |
1088 | |
1089 /* FIXME: clear selection on most commands */ | |
1090 /* FIXME: don't include escape when repeating change with '.'? */ | |
1091 static struct action | |
1092 change(ledit_view *view, char *text, size_t len) { | |
1093 (void)text; | |
1094 (void)len; | |
1095 motion_callback cb = NULL; | |
1096 int num = get_key_repeat_and_motion_cb(view, &cb); | |
1097 if (num == -1) | |
1098 return err_invalid_key(view); | |
1099 if (view->mode == VISUAL) { | |
1100 view_set_mode(view, INSERT); | |
1101 delete_selection(view); | |
1102 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1103 clear_key_stack(); | |
1104 } else { | |
1105 if (cb == &change_cb) { | |
1106 int lines = num > 0 ? num : 1; | |
1107 size_t new_line; | |
1108 int new_softline; | |
1109 get_new_line_softline( | |
1110 view, view->cur_line, view->cur_index, | |
1111 lines - 1, &new_line, &new_softline | |
1112 ); | |
1113 size_t start, end; | |
1114 view_get_softline_bounds(view, new_line, new_sof… | |
1115 cb(view, new_line, start, KEY_MOTION_LINE); | |
1116 clear_key_stack(); | |
1117 } else if (cb != NULL) { | |
1118 return err_invalid_key(view); | |
1119 } else { | |
1120 struct key_stack_elem *e = push_key_stack(); | |
1121 e->key = KEY_MOTIONALLOWED|KEY_NUMBERALLOWED; | |
1122 e->count = num; | |
1123 e->motion_cb = &change_cb; | |
1124 } | |
1125 } | |
1126 return (struct action){ACTION_NONE, NULL}; | |
1127 } | |
1128 | |
1129 static void | |
1130 change_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type … | |
1131 CHECK_VIEW_LOCKED_NORETURN(view); | |
1132 /* set mode first so the deletion is included in the undo group … | |
1133 view_set_mode(view, INSERT); | |
1134 int line_based = type == KEY_MOTION_LINE ? 1 : 0; | |
1135 /* this hackery is needed to avoid deleting the entire last line… | |
1136 instead leave an empty line - this should be made nicer (FIXM… | |
1137 size_t pos1 = view->cur_index, pos2 = char_pos; | |
1138 if (line_based && !view->buffer->hard_line_based) { | |
1139 size_t pos1, pos2, tmp; | |
1140 view_get_pos_softline_bounds(view, view->cur_line, view-… | |
1141 view_get_pos_softline_bounds(view, line, char_pos, &tmp,… | |
1142 } else if (line_based && view->buffer->hard_line_based) { | |
1143 pos1 = 0; | |
1144 ledit_line *ll = buffer_get_line(view->buffer, line); | |
1145 pos2 = ll->len; | |
1146 } | |
1147 /* force line_based to 0 (see comment about hackery above) */ | |
1148 delete_range( | |
1149 view, 0, 0, | |
1150 view->cur_line, pos1, | |
1151 line, pos2, view->mode != INSERT | |
1152 ); | |
1153 if (view->mode != INSERT) | |
1154 paste_buffer_line_based = line_based; | |
1155 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1156 } | |
1157 | |
1158 static struct action | |
1159 yank(ledit_view *view, char *text, size_t len) { | |
1160 (void)text; | |
1161 (void)len; | |
1162 if (!paste_buffer) | |
1163 paste_buffer = txtbuf_new(); | |
1164 if (view->mode == VISUAL) { | |
1165 sort_range( | |
1166 &view->sel.line1, &view->sel.byte1, &view->sel.line2… | |
1167 ); | |
1168 buffer_copy_text_to_txtbuf( | |
1169 view->buffer, paste_buffer, | |
1170 view->sel.line1, view->sel.byte1, view->sel.line2, v… | |
1171 ); | |
1172 paste_buffer_line_based = 0; | |
1173 view->cur_line = view->sel.line1; | |
1174 view->cur_index = view->sel.byte1; | |
1175 view_wipe_selection(view); | |
1176 view_set_mode(view, NORMAL); | |
1177 view->cur_index = view_get_legal_normal_pos( | |
1178 view, view->cur_line, view->cur_index | |
1179 ); | |
1180 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1181 clear_key_stack(); | |
1182 } else { | |
1183 motion_callback cb = NULL; | |
1184 int num = get_key_repeat_and_motion_cb(view, &cb); | |
1185 if (num == -1) | |
1186 return err_invalid_key(view); | |
1187 if (cb == &yank_cb) { | |
1188 if (num == 0) | |
1189 num = 1; | |
1190 size_t new_line; | |
1191 int new_softline; | |
1192 get_new_line_softline( | |
1193 view, view->cur_line, view->cur_index, | |
1194 num - 1, &new_line, &new_softline | |
1195 ); | |
1196 size_t start, end; | |
1197 view_get_softline_bounds(view, new_line, new_sof… | |
1198 cb(view, new_line, start, KEY_MOTION_LINE); | |
1199 clear_key_stack(); | |
1200 } else if (cb == NULL) { | |
1201 struct key_stack_elem *e = push_key_stack(); | |
1202 e->key = KEY_MOTIONALLOWED|KEY_NUMBERALLOWED; | |
1203 e->count = num; | |
1204 e->motion_cb = &yank_cb; | |
1205 } else { | |
1206 /* FIXME: proper error */ | |
1207 clear_key_stack(); | |
1208 } | |
1209 } | |
1210 discard_repetition_stack(); | |
1211 return (struct action){ACTION_NONE, NULL}; | |
1212 } | |
1213 | |
1214 static struct action | |
1215 yank_lines(ledit_view *view, char *text, size_t len) { | |
1216 (void)text; | |
1217 (void)len; | |
1218 int num = get_key_repeat(); | |
1219 if (num == -1) | |
1220 return err_invalid_key(view); | |
1221 else if (num == 0) | |
1222 num = 1; | |
1223 size_t new_line; | |
1224 int new_softline; | |
1225 get_new_line_softline( | |
1226 view, view->cur_line, view->cur_index, | |
1227 num - 1, &new_line, &new_softline | |
1228 ); | |
1229 size_t start, end; | |
1230 view_get_softline_bounds(view, new_line, new_softline, &start, &… | |
1231 yank_cb(view, new_line, start, KEY_MOTION_LINE); | |
1232 clear_key_stack(); | |
1233 return (struct action){ACTION_NONE, NULL}; | |
1234 } | |
1235 | |
1236 static void | |
1237 yank_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type ty… | |
1238 int line_based = type == KEY_MOTION_LINE ? 1 : 0; | |
1239 size_t l1 = view->cur_line, l2 = line, b1 = view->cur_index, b2 … | |
1240 if (!paste_buffer) | |
1241 paste_buffer = txtbuf_new(); | |
1242 if (l2 < l1 || (l1 == l2 && b2 < b1)) { | |
1243 swap_sz(&l1, &l2); | |
1244 swap_sz(&b1, &b2); | |
1245 } | |
1246 if (line_based && !view->buffer->hard_line_based) { | |
1247 size_t start1, end2, tmp; | |
1248 view_get_pos_softline_bounds(view, l1, b1, &start1, &tmp… | |
1249 view_get_pos_softline_bounds(view, l2, b2, &tmp, &end2); | |
1250 ledit_line *ll = buffer_get_line(view->buffer, l2); | |
1251 if (end2 == ll->len && l2 < view->lines_num - 1) { | |
1252 l2++; | |
1253 end2 = 0; | |
1254 } | |
1255 buffer_copy_text_to_txtbuf( | |
1256 view->buffer, paste_buffer, l1, start1, l2, end2 | |
1257 ); | |
1258 } else if (line_based && view->buffer->hard_line_based) { | |
1259 ledit_line *ll = buffer_get_line(view->buffer, l2); | |
1260 size_t end = ll->len; | |
1261 if (l2 < view->lines_num - 1) { | |
1262 l2++; | |
1263 end = 0; | |
1264 } | |
1265 buffer_copy_text_to_txtbuf( | |
1266 view->buffer, paste_buffer, l1, 0, l2, end | |
1267 ); | |
1268 } else { | |
1269 buffer_copy_text_to_txtbuf( | |
1270 view->buffer, paste_buffer, l1, b1, l2, b2 | |
1271 ); | |
1272 } | |
1273 paste_buffer_line_based = line_based; | |
1274 discard_repetition_stack(); | |
1275 } | |
1276 | |
1277 static struct action | |
1278 delete(ledit_view *view, char *text, size_t len) { | |
1279 (void)text; | |
1280 (void)len; | |
1281 motion_callback cb = NULL; | |
1282 int num = get_key_repeat_and_motion_cb(view, &cb); | |
1283 if (num == -1) | |
1284 return err_invalid_key(view); | |
1285 if (delete_selection(view)) { | |
1286 clear_key_stack(); | |
1287 } else { | |
1288 /* FIXME: checking equality of the function pointer may … | |
1289 /* -> actually, it shouldn't be a problem */ | |
1290 if (cb == &delete_cb) { | |
1291 int lines = num > 0 ? num : 1; | |
1292 size_t new_line; | |
1293 int new_softline; | |
1294 get_new_line_softline( | |
1295 view, view->cur_line, view->cur_index, | |
1296 lines - 1, &new_line, &new_softline | |
1297 ); | |
1298 size_t start, end; | |
1299 view_get_softline_bounds(view, new_line, new_sof… | |
1300 cb(view, new_line, start, KEY_MOTION_LINE); | |
1301 clear_key_stack(); | |
1302 } else if (cb != NULL) { | |
1303 return err_invalid_key(view); | |
1304 } else { | |
1305 struct key_stack_elem *e = push_key_stack(); | |
1306 e->key = KEY_MOTIONALLOWED|KEY_NUMBERALLOWED; | |
1307 e->count = num; | |
1308 e->motion_cb = &delete_cb; | |
1309 } | |
1310 } | |
1311 return (struct action){ACTION_NONE, NULL}; | |
1312 } | |
1313 | |
1314 /* FIXME: should this get number of lines to remove or actual end line? … | |
1315 static void | |
1316 delete_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type … | |
1317 CHECK_VIEW_LOCKED_NORETURN(view); | |
1318 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1319 int line_based = type == KEY_MOTION_LINE ? 1 : 0; | |
1320 delete_range( | |
1321 view, line_based, 0, | |
1322 view->cur_line, view->cur_index, | |
1323 line, char_pos, view->mode != INSERT | |
1324 ); | |
1325 if (view->mode != INSERT) { | |
1326 paste_buffer_line_based = line_based; | |
1327 finalize_repetition_stack(); | |
1328 } | |
1329 if (view->mode == NORMAL) | |
1330 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1331 } | |
1332 | |
1333 /* Note that these paste functions are a bit weird when working with sof… | |
1334 they always make sure the pasted text is separated from the surroundi… | |
1335 hard lines, which may be unexpected, but the alternatives I could thi… | |
1336 even weirder */ | |
1337 static struct action | |
1338 paste_normal(ledit_view *view, char *text, size_t len) { | |
1339 (void)text; | |
1340 (void)len; | |
1341 if (!paste_buffer) { | |
1342 window_show_message(view->window, "Nothing to paste", -1… | |
1343 discard_repetition_stack(); | |
1344 return (struct action){ACTION_NONE, NULL}; | |
1345 } | |
1346 if (paste_buffer_line_based) { | |
1347 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1348 ledit_line *ll = buffer_get_line(view->buffer, view->cur… | |
1349 size_t brk = 0; | |
1350 if (view->buffer->hard_line_based) { | |
1351 brk = ll->len; | |
1352 } else { | |
1353 size_t tmp; | |
1354 view_get_pos_softline_bounds(view, view->cur_lin… | |
1355 } | |
1356 /* FIXME: this is a bit inefficient because insert_text … | |
1357 use the *_base functions, but maybe this way is a bit… | |
1358 insert_text( | |
1359 view, view->cur_line, brk, | |
1360 "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1… | |
1361 ); | |
1362 size_t text_len = paste_buffer->len; | |
1363 ll = buffer_get_line(view->buffer, view->cur_line + 1); | |
1364 if (ll->len == 0 && paste_buffer->text[text_len-1] == '\… | |
1365 /* remove trailing newline if it exists and text… | |
1366 text_len--; | |
1367 paste_buffer->text[text_len] = '\0'; | |
1368 } else if (ll->len > 0 && paste_buffer->text[text_len-1]… | |
1369 /* ensure pasted text is on its own hard line */ | |
1370 insert_text( | |
1371 view, view->cur_line + 1, 0, | |
1372 "\n", 1, 0, 0, view->cur_line, view->cur_ind… | |
1373 ); | |
1374 } | |
1375 insert_text( | |
1376 view, view->cur_line + 1, 0, | |
1377 paste_buffer->text, text_len, 0, 0, view->cur_line +… | |
1378 ); | |
1379 } else { | |
1380 size_t old_line = view->cur_line; | |
1381 size_t old_index = view->cur_index; | |
1382 /* must allow illegal index so text can be pasted at end… | |
1383 move_cursor_logically(view, 1, 1); | |
1384 insert_text( | |
1385 view, view->cur_line, view->cur_index, | |
1386 paste_buffer->text, paste_buffer->len, | |
1387 old_line, old_index, view->cur_line, view->cur_index… | |
1388 ); | |
1389 } | |
1390 if (view->mode == NORMAL) | |
1391 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1392 if (view->mode != INSERT) | |
1393 finalize_repetition_stack(); | |
1394 return (struct action){ACTION_NONE, NULL}; | |
1395 } | |
1396 | |
1397 static struct action | |
1398 paste_normal_backwards(ledit_view *view, char *text, size_t len) { | |
1399 (void)text; | |
1400 (void)len; | |
1401 if (!paste_buffer) { | |
1402 window_show_message(view->window, "Nothing to paste", -1… | |
1403 discard_repetition_stack(); | |
1404 return (struct action){ACTION_NONE, NULL}; | |
1405 } | |
1406 if (paste_buffer_line_based) { | |
1407 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1408 size_t brk = 0; | |
1409 if (!view->buffer->hard_line_based) { | |
1410 size_t tmp; | |
1411 view_get_pos_softline_bounds(view, view->cur_lin… | |
1412 } | |
1413 /* FIXME: better interface without these weird int args … | |
1414 insert_text( | |
1415 view, view->cur_line, brk, | |
1416 "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1… | |
1417 ); | |
1418 size_t text_len = paste_buffer->len; | |
1419 ledit_line *ll = buffer_get_line(view->buffer, view->cur… | |
1420 if (paste_buffer->text[text_len-1] == '\n') { | |
1421 /* remove trailing newline if it exists */ | |
1422 text_len--; | |
1423 paste_buffer->text[text_len] = '\0'; | |
1424 } | |
1425 size_t new_line = view->cur_line; | |
1426 if (ll->len > 0) { | |
1427 /* ensure pasted text is on its own hard line */ | |
1428 insert_text( | |
1429 view, view->cur_line, ll->len, | |
1430 "\n", 1, 0, 0, view->cur_line, view->cur_ind… | |
1431 ); | |
1432 new_line = view->cur_line + 1; | |
1433 } | |
1434 insert_text( | |
1435 view, new_line, 0, | |
1436 paste_buffer->text, text_len, 0, 0, new_line, 0, 0, … | |
1437 ); | |
1438 } else { | |
1439 insert_text( | |
1440 view, view->cur_line, view->cur_index, | |
1441 paste_buffer->text, paste_buffer->len, | |
1442 0, 0, view->cur_line, view->cur_index, 0, 1, 1 | |
1443 ); | |
1444 } | |
1445 if (view->mode == NORMAL) | |
1446 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1447 if (view->mode != INSERT) | |
1448 finalize_repetition_stack(); | |
1449 return (struct action){ACTION_NONE, NULL}; | |
1450 } | |
1451 | |
1452 static struct action | |
1453 key_0(ledit_view *view, char *text, size_t len) { | |
1454 struct key_stack_elem *e = peek_key_stack(); | |
1455 if (!e || (e->key & KEY_MOTIONALLOWED)) { | |
1456 return cursor_to_beginning(view, text, len); | |
1457 } else if (e->key & KEY_NUMBER) { | |
1458 return push_0(view, text, len); | |
1459 } else { | |
1460 return err_invalid_key(view); | |
1461 } | |
1462 } | |
1463 | |
1464 static void | |
1465 push_num(ledit_view *view, int num) { | |
1466 struct key_stack_elem *e = peek_key_stack(); | |
1467 if (!e || !(e->key & KEY_NUMBER)) { | |
1468 e = push_key_stack(); | |
1469 e->key = KEY_NUMBER; | |
1470 e->followup = KEY_NUMBER|KEY_NUMBERALLOWED; | |
1471 } | |
1472 if (INT_MAX / 10 < e->count) { | |
1473 window_show_message( | |
1474 view->window, | |
1475 "Integer overflow in key repetition", -1 | |
1476 ); | |
1477 clear_key_stack(); | |
1478 return; | |
1479 } | |
1480 e->count *= 10; | |
1481 if (INT_MAX - num < e->count) { | |
1482 window_show_message( | |
1483 view->window, | |
1484 "Integer overflow in key repetition", -1 | |
1485 ); | |
1486 clear_key_stack(); | |
1487 return; | |
1488 } | |
1489 e->count += num; | |
1490 } | |
1491 | |
1492 static struct action | |
1493 push_0(ledit_view *view, char *text, size_t len) { | |
1494 (void)view; | |
1495 (void)text; | |
1496 (void)len; | |
1497 push_num(view, 0); | |
1498 return (struct action){ACTION_NONE, NULL}; | |
1499 } | |
1500 | |
1501 static struct action | |
1502 push_1(ledit_view *view, char *text, size_t len) { | |
1503 (void)view; | |
1504 (void)text; | |
1505 (void)len; | |
1506 push_num(view, 1); | |
1507 return (struct action){ACTION_NONE, NULL}; | |
1508 } | |
1509 | |
1510 static struct action | |
1511 push_2(ledit_view *view, char *text, size_t len) { | |
1512 (void)view; | |
1513 (void)text; | |
1514 (void)len; | |
1515 push_num(view, 2); | |
1516 return (struct action){ACTION_NONE, NULL}; | |
1517 } | |
1518 | |
1519 static struct action | |
1520 push_3(ledit_view *view, char *text, size_t len) { | |
1521 (void)view; | |
1522 (void)text; | |
1523 (void)len; | |
1524 push_num(view, 3); | |
1525 return (struct action){ACTION_NONE, NULL}; | |
1526 } | |
1527 | |
1528 static struct action | |
1529 push_4(ledit_view *view, char *text, size_t len) { | |
1530 (void)view; | |
1531 (void)text; | |
1532 (void)len; | |
1533 push_num(view, 4); | |
1534 return (struct action){ACTION_NONE, NULL}; | |
1535 } | |
1536 | |
1537 static struct action | |
1538 push_5(ledit_view *view, char *text, size_t len) { | |
1539 (void)view; | |
1540 (void)text; | |
1541 (void)len; | |
1542 push_num(view, 5); | |
1543 return (struct action){ACTION_NONE, NULL}; | |
1544 } | |
1545 | |
1546 static struct action | |
1547 push_6(ledit_view *view, char *text, size_t len) { | |
1548 (void)view; | |
1549 (void)text; | |
1550 (void)len; | |
1551 push_num(view, 6); | |
1552 return (struct action){ACTION_NONE, NULL}; | |
1553 } | |
1554 | |
1555 static struct action | |
1556 push_7(ledit_view *view, char *text, size_t len) { | |
1557 (void)view; | |
1558 (void)text; | |
1559 (void)len; | |
1560 push_num(view, 7); | |
1561 return (struct action){ACTION_NONE, NULL}; | |
1562 } | |
1563 | |
1564 static struct action | |
1565 push_8(ledit_view *view, char *text, size_t len) { | |
1566 (void)view; | |
1567 (void)text; | |
1568 (void)len; | |
1569 push_num(view, 8); | |
1570 return (struct action){ACTION_NONE, NULL}; | |
1571 } | |
1572 | |
1573 static struct action | |
1574 push_9(ledit_view *view, char *text, size_t len) { | |
1575 (void)view; | |
1576 (void)text; | |
1577 (void)len; | |
1578 push_num(view, 9); | |
1579 return (struct action){ACTION_NONE, NULL}; | |
1580 } | |
1581 | |
1582 /* FIXME: function to look at pango property to decide when to delete en… | |
1583 /* FIXME: The cursor may be in an illegal position after one of the dele… | |
1584 functions, but calling get_legal_normal_pos also would be weird becau… | |
1585 wouldn't necessarily be at the deletion index anymore */ | |
1586 #define GEN_DELETE_FUNCS(type, next_func, prev_func) … | |
1587 static struct action … | |
1588 delete_##type##_backwards_base(ledit_view *view, char *text, size_t len,… | |
1589 (void)text; … | |
1590 (void)len; … | |
1591 int num = get_key_repeat(); … | |
1592 if (num == -1) { … | |
1593 window_show_message(view->window, "Invalid key", -1); … | |
1594 return (struct action){ACTION_NONE, NULL}; … | |
1595 } else if (num == 0) { … | |
1596 num = 1; … | |
1597 } … | |
1598 size_t start_line, start_index; … | |
1599 prev_func( … | |
1600 view, view->cur_line, view->cur_index, … | |
1601 num, multiline, &start_line, &start_index … | |
1602 ); … | |
1603 delete_range( … | |
1604 view, 0, 0, … | |
1605 start_line, start_index, … | |
1606 view->cur_line, view->cur_index, view->mode != INSERT … | |
1607 ); … | |
1608 view->cur_line = start_line; … | |
1609 view->cur_index = start_index; … | |
1610 if (view->mode == NORMAL) … | |
1611 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1612 if (view->mode != INSERT) { … | |
1613 paste_buffer_line_based = 0; … | |
1614 finalize_repetition_stack(); … | |
1615 } … | |
1616 return (struct action){ACTION_NONE, NULL}; … | |
1617 } … | |
1618 … | |
1619 static struct action … | |
1620 delete_##type##_backwards(ledit_view *view, char *text, size_t len) { … | |
1621 return delete_##type##_backwards_base(view, text, len, 0); … | |
1622 } … | |
1623 … | |
1624 static struct action … | |
1625 delete_##type##_backwards_multiline(ledit_view *view, char *text, size_t… | |
1626 return delete_##type##_backwards_base(view, text, len, 1); … | |
1627 } … | |
1628 … | |
1629 static struct action … | |
1630 delete_##type##_forwards_base(ledit_view *view, char *text, size_t len, … | |
1631 (void)text; … | |
1632 (void)len; … | |
1633 int num = get_key_repeat(); … | |
1634 if (num == -1) { … | |
1635 window_show_message(view->window, "Invalid key", -1); … | |
1636 return (struct action){ACTION_NONE, NULL}; … | |
1637 } else if (num == 0) { … | |
1638 num = 1; … | |
1639 } … | |
1640 size_t end_line, end_index; … | |
1641 next_func( … | |
1642 view, view->cur_line, view->cur_index, … | |
1643 num, multiline, &end_line, &end_index … | |
1644 ); … | |
1645 delete_range( … | |
1646 view, 0, 0, … | |
1647 view->cur_line, view->cur_index, … | |
1648 end_line, end_index, view->mode != INSERT … | |
1649 ); … | |
1650 if (view->mode == NORMAL) … | |
1651 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1652 if (view->mode != INSERT) { … | |
1653 paste_buffer_line_based = 0; … | |
1654 finalize_repetition_stack(); … | |
1655 } … | |
1656 return (struct action){ACTION_NONE, NULL}; … | |
1657 } … | |
1658 … | |
1659 static struct action … | |
1660 delete_##type##_forwards(ledit_view *view, char *text, size_t len) { … | |
1661 return delete_##type##_forwards_base(view, text, len, 0); … | |
1662 } … | |
1663 … | |
1664 static struct action … | |
1665 delete_##type##_forwards_multiline(ledit_view *view, char *text, size_t … | |
1666 return delete_##type##_forwards_base(view, text, len, 1); … | |
1667 } | |
1668 | |
1669 /* Yes, I know, all these helpers are ugly... */ | |
1670 #define buffer_next_char_pos_helper(view, line, byte, num, multiline, li… | |
1671 buffer_next_char_pos((view)->buffer, line, byte, num, multiline, lin… | |
1672 #define buffer_prev_char_pos_helper(view, line, byte, num, multiline, li… | |
1673 buffer_prev_char_pos((view)->buffer, line, byte, num, multiline, lin… | |
1674 | |
1675 GEN_DELETE_FUNCS(graphemes, view_next_cursor_pos, view_prev_cursor_pos) | |
1676 GEN_DELETE_FUNCS(chars, buffer_next_char_pos_helper, buffer_prev_char_po… | |
1677 | |
1678 static struct action | |
1679 move_to_eol(ledit_view *view, char *text, size_t len) { | |
1680 (void)text; | |
1681 (void)len; | |
1682 motion_callback cb; | |
1683 int num = get_key_repeat_and_motion_cb(view, &cb); | |
1684 if (num == -1) | |
1685 return err_invalid_key(view); | |
1686 if (num == 0) | |
1687 num = 1; | |
1688 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1689 size_t new_line; | |
1690 int new_softline; | |
1691 get_new_line_softline( | |
1692 view, view->cur_line, view->cur_index, num - 1, | |
1693 &new_line, &new_softline | |
1694 ); | |
1695 ledit_line *ll = buffer_get_line(view->buffer, new_line); | |
1696 size_t end_index = ll->len; | |
1697 if (!view->buffer->hard_line_based) { | |
1698 size_t tmp; | |
1699 view_get_softline_bounds(view, new_line, new_softline, &… | |
1700 } | |
1701 if (cb != NULL) { | |
1702 cb(view, new_line, end_index, KEY_MOTION_CHAR); | |
1703 } else { | |
1704 view->cur_line = new_line; | |
1705 view->cur_index = end_index; | |
1706 if (view->mode == VISUAL) { | |
1707 view_set_selection( | |
1708 view, | |
1709 view->sel.line1, view->sel.byte1, | |
1710 new_line, end_index | |
1711 ); | |
1712 } else if (view->mode == NORMAL) { | |
1713 /* FIXME: this is weird because the cursor is ac… | |
1714 next soft line, but the alternative has too m… | |
1715 with bidi text */ | |
1716 view->cur_index = view_get_legal_normal_pos( | |
1717 view, view->cur_line, view->cur_index | |
1718 ); | |
1719 view_set_line_cursor_attrs(view, view->cur_line,… | |
1720 } | |
1721 } | |
1722 return (struct action){ACTION_NONE, NULL}; | |
1723 } | |
1724 | |
1725 #define GEN_WORD_MOVEMENT(name, func) … | |
1726 static struct action … | |
1727 name(ledit_view *view, char *text, size_t len) { … | |
1728 (void)text; … | |
1729 (void)len; … | |
1730 motion_callback cb; … | |
1731 int num = get_key_repeat_and_motion_cb(view, &cb); … | |
1732 if (num == -1) … | |
1733 return err_invalid_key(view); … | |
1734 if (num == 0) … | |
1735 num = 1; … | |
1736 size_t new_line, new_index, new_real_index; … | |
1737 func( … | |
1738 view, … | |
1739 view->cur_line, view->cur_index, num, … | |
1740 &new_line, &new_index, &new_real_index … | |
1741 ); … | |
1742 if (cb != NULL) { … | |
1743 cb(view, new_line, new_real_index, KEY_MOTION_CHAR); … | |
1744 } else { … | |
1745 if (view->mode == VISUAL) { … | |
1746 view_set_selection( … | |
1747 view, … | |
1748 view->sel.line1, view->sel.byte1, … | |
1749 new_line, new_real_index … | |
1750 ); … | |
1751 view->cur_line = new_line; … | |
1752 view->cur_index = new_real_index; … | |
1753 } else { … | |
1754 if (new_line != view->cur_line) … | |
1755 view_wipe_line_cursor_attrs( … | |
1756 view, view->cur_line … | |
1757 ); … | |
1758 view->cur_line = new_line; … | |
1759 view->cur_index = new_index; … | |
1760 if (view->mode == NORMAL) { … | |
1761 view_set_line_cursor_attrs( … | |
1762 view, view->cur_line, view->cur_inde… | |
1763 ); … | |
1764 } … | |
1765 } … | |
1766 discard_repetition_stack(); … | |
1767 } … | |
1768 clear_key_stack(); … | |
1769 return (struct action){ACTION_NONE, NULL}; … | |
1770 } | |
1771 | |
1772 GEN_WORD_MOVEMENT(next_word, view_next_word) | |
1773 GEN_WORD_MOVEMENT(next_word_end, view_next_word_end) | |
1774 GEN_WORD_MOVEMENT(next_bigword, view_next_bigword) | |
1775 GEN_WORD_MOVEMENT(next_bigword_end, view_next_bigword_end) | |
1776 GEN_WORD_MOVEMENT(prev_word, view_prev_word) | |
1777 GEN_WORD_MOVEMENT(prev_bigword, view_prev_bigword) | |
1778 | |
1779 static void | |
1780 move_cursor_left_right(ledit_view *view, int dir, int allow_illegal_inde… | |
1781 motion_callback cb; | |
1782 int num = get_key_repeat_and_motion_cb(view, &cb); | |
1783 if (num == -1) | |
1784 (void)err_invalid_key(view); /* FIXME: why do I not retu… | |
1785 if (num == 0) | |
1786 num = 1; | |
1787 | |
1788 ledit_line *cur_line = buffer_get_line(view->buffer, view->cur_l… | |
1789 /* FIXME: standardize interface - num * dir or separately? */ | |
1790 size_t last_index; | |
1791 size_t new_index = view_move_cursor_visually( | |
1792 view, view->cur_line, view->cur_index, num * dir, &last_index | |
1793 ); | |
1794 /* when in normal mode, the cursor cannot be at the very end | |
1795 of the line because it's always covering a character */ | |
1796 if (new_index >= cur_line->len) { | |
1797 if (!allow_illegal_index && | |
1798 view->mode == NORMAL && cb == NULL) { | |
1799 new_index = last_index; | |
1800 } else { | |
1801 /* FIXME: I guess this is unnecessary */ | |
1802 new_index = cur_line->len; | |
1803 } | |
1804 } | |
1805 if (cb != NULL) { | |
1806 cb(view, view->cur_line, new_index, KEY_MOTION_CHAR); | |
1807 } else { | |
1808 view->cur_index = new_index; | |
1809 if (view->mode == VISUAL) { | |
1810 /* FIXME: check if view->sel_valid and only use … | |
1811 view_set_selection(view, view->sel.line1, view->… | |
1812 } else if (view->mode == INSERT && view->sel_valid) { | |
1813 /* FIXME: I guess this is unnecessary now that no | |
1814 selection is allowed in insert mode */ | |
1815 view_wipe_selection(view); | |
1816 } else if (view->mode == NORMAL) { | |
1817 view_set_line_cursor_attrs(view, view->cur_line,… | |
1818 } | |
1819 view->redraw = 1; | |
1820 discard_repetition_stack(); | |
1821 } | |
1822 clear_key_stack(); | |
1823 } | |
1824 | |
1825 static struct action | |
1826 cursor_left(ledit_view *view, char *text, size_t len) { | |
1827 (void)text; | |
1828 (void)len; | |
1829 move_cursor_left_right(view, -1, 0); | |
1830 return (struct action){ACTION_NONE, NULL}; | |
1831 } | |
1832 | |
1833 static struct action | |
1834 cursor_right(ledit_view *view, char *text, size_t len) { | |
1835 (void)text; | |
1836 (void)len; | |
1837 move_cursor_left_right(view, 1, 0); | |
1838 return (struct action){ACTION_NONE, NULL}; | |
1839 } | |
1840 | |
1841 static struct action | |
1842 break_line(ledit_view *view, char *text, size_t len) { | |
1843 (void)text; | |
1844 (void)len; | |
1845 int start_group = 1; | |
1846 /* FIXME: this is unnecessary now because no selection is suppor… | |
1847 if (delete_selection(view)) | |
1848 start_group = 0; | |
1849 if (view->mode == NORMAL) | |
1850 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1851 insert_text(view, view->cur_line, view->cur_index, "\n", 1, 0, 0… | |
1852 if (view->mode == NORMAL) { | |
1853 view->cur_index = view_get_legal_normal_pos(view, view->… | |
1854 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1855 } | |
1856 return (struct action){ACTION_NONE, NULL}; | |
1857 } | |
1858 | |
1859 static void | |
1860 move_cursor_logically(ledit_view *view, int movement_dir, int allow_ille… | |
1861 if (movement_dir < 0) { | |
1862 view_prev_cursor_pos( | |
1863 view, view->cur_line, view->cur_index, 1, 0, NULL, &… | |
1864 ); | |
1865 } else { | |
1866 view_next_cursor_pos( | |
1867 view, view->cur_line, view->cur_index, 1, 0, NULL, &… | |
1868 ); | |
1869 } | |
1870 if (!allow_illegal_index) { | |
1871 view->cur_index = view_get_legal_normal_pos( | |
1872 view, view->cur_line, view->cur_index | |
1873 ); | |
1874 } | |
1875 } | |
1876 | |
1877 static struct action | |
1878 return_to_normal(ledit_view *view, char *text, size_t len) { | |
1879 (void)text; | |
1880 (void)len; | |
1881 clear_key_stack(); | |
1882 if (view->mode == INSERT) | |
1883 finalize_repetition_stack(); | |
1884 /* FIXME: I guess this is unnecessary now that insert mode does … | |
1885 if (view->mode == INSERT && view->sel_valid) { | |
1886 view_set_mode(view, VISUAL); | |
1887 } else if (view->mode != NORMAL) { | |
1888 view_set_mode(view, NORMAL); | |
1889 move_cursor_logically(view, -1, 0); | |
1890 view_wipe_selection(view); | |
1891 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
1892 } | |
1893 return (struct action){ACTION_NONE, NULL}; | |
1894 } | |
1895 | |
1896 static struct action | |
1897 enter_insert(ledit_view *view, char *text, size_t len) { | |
1898 (void)text; | |
1899 (void)len; | |
1900 if (view->mode == VISUAL) { | |
1901 view_wipe_selection(view); | |
1902 } | |
1903 view_wipe_line_cursor_attrs(view, view->cur_line); | |
1904 view_set_mode(view, INSERT); | |
1905 clear_key_stack(); | |
1906 return (struct action){ACTION_NONE, NULL}; | |
1907 } | |
1908 | |
1909 /* FIXME: Check if previous key allows motion command - or should this b… | |
1910 static void | |
1911 move_cursor_up_down(ledit_view *view, int dir) { | |
1912 size_t new_line; | |
1913 int new_softline; | |
1914 | |
1915 motion_callback cb; | |
1916 int num = get_key_repeat_and_motion_cb(view, &cb); | |
1917 if (num == -1) | |
1918 (void)err_invalid_key(view); | |
1919 if (num == 0) | |
1920 num = 1; | |
1921 num *= dir; | |
1922 | |
1923 get_new_line_softline( | |
1924 view, view->cur_line, view->cur_index, | |
1925 num, &new_line, &new_softline | |
1926 ); | |
1927 | |
1928 if (cb != NULL) { | |
1929 size_t start, end; | |
1930 view_get_softline_bounds(view, new_line, new_softline, &… | |
1931 cb(view, new_line, start, KEY_MOTION_LINE); | |
1932 } else { | |
1933 int lineno, x, diff = 0, old_line = view->cur_line; | |
1934 view_pos_to_x_softline(view, view->cur_line, view->cur_i… | |
1935 view->cur_index = view_x_softline_to_pos(view, new_line,… | |
1936 if (view->cur_line != new_line) | |
1937 diff = 1; | |
1938 view->cur_line = new_line; | |
1939 | |
1940 if (view->mode == VISUAL) { | |
1941 view_set_selection(view, view->sel.line1, view->… | |
1942 } else if (view->mode == NORMAL) { | |
1943 if (diff) | |
1944 view_wipe_line_cursor_attrs(view, old_li… | |
1945 view_set_line_cursor_attrs(view, view->cur_line,… | |
1946 } | |
1947 view->redraw = 1; | |
1948 discard_repetition_stack(); | |
1949 } | |
1950 clear_key_stack(); | |
1951 } | |
1952 | |
1953 static struct action | |
1954 cursor_down(ledit_view *view, char *text, size_t len) { | |
1955 (void)text; | |
1956 (void)len; | |
1957 move_cursor_up_down(view, 1); | |
1958 return (struct action){ACTION_NONE, NULL}; | |
1959 } | |
1960 | |
1961 static struct action | |
1962 cursor_up(ledit_view *view, char *text, size_t len) { | |
1963 (void)text; | |
1964 (void)len; | |
1965 move_cursor_up_down(view, -1); | |
1966 return (struct action){ACTION_NONE, NULL}; | |
1967 } | |
1968 | |
1969 static struct action | |
1970 join_lines(ledit_view *view, char *text, size_t len) { | |
1971 (void)text; | |
1972 (void)len; | |
1973 int num = get_key_repeat(); | |
1974 if (num == -1) | |
1975 return err_invalid_key(view); | |
1976 if (num == 0) | |
1977 num = 1; | |
1978 int start_group = 1; | |
1979 ledit_line *ll1; | |
1980 ledit_line *ll2; | |
1981 size_t cur_line = view->cur_line; | |
1982 /* don't return yet so the stuff at the bottom gets called, | |
1983 in particular finalize_repetition_stack */ | |
1984 if (cur_line == view->lines_num - 1) | |
1985 window_show_message(view->window, "No following lines to… | |
1986 for (int i = 0; i < num; i++) { | |
1987 if (cur_line == view->lines_num - 1) | |
1988 break; | |
1989 ll1 = buffer_get_line(view->buffer, cur_line); | |
1990 ll2 = buffer_get_line(view->buffer, cur_line + 1); | |
1991 /* figure out if the current line ends in whitespace - | |
1992 this could probably be improved */ | |
1993 size_t last_char_byte = line_prev_utf8(ll1, ll1->len); | |
1994 size_t last_ws = view_line_next_non_whitespace(view, cur… | |
1995 int end_in_ws = (last_ws == ll1->len); /* also works if … | |
1996 size_t start_idx = view_line_next_non_whitespace(view, c… | |
1997 /* save len here because view_delete_range_base calls vi… | |
1998 so the returned index may not be right for the follow… | |
1999 /* although, on second thought, that only happens when t… | |
2000 which is a special case that is ignored below... */ | |
2001 size_t len = ll1->len; | |
2002 size_t len2 = ll2->len; | |
2003 view_delete_range_base( | |
2004 view, DELETE_CHAR, start_group, | |
2005 cur_line, ll1->len, cur_line + 1, start_idx, | |
2006 &view->cur_line, &view->cur_index, NULL | |
2007 ); | |
2008 /* insert space if there is no other whitespace */ | |
2009 if (!end_in_ws && len2 > 0) { | |
2010 ledit_range cur_range = {.line1 = view->cur_line… | |
2011 buffer_insert_with_undo_base( | |
2012 view->buffer, cur_range, 1, 0, | |
2013 view->mode, cur_line, len, | |
2014 " ", 1, &view->cur_line, &view->cur_index | |
2015 ); | |
2016 } | |
2017 start_group = 0; | |
2018 } | |
2019 buffer_recalc_all_views_from_line(view->buffer, cur_line); | |
2020 /* FIXME: should view_set_line_cursor_attrs just have this check… | |
2021 if (view->mode == NORMAL) { | |
2022 view->cur_index = view_get_legal_normal_pos(view, view->… | |
2023 view_set_line_cursor_attrs( | |
2024 view, view->cur_line, view->cur_index | |
2025 ); | |
2026 } | |
2027 if (view->mode != INSERT) | |
2028 finalize_repetition_stack(); | |
2029 return (struct action){ACTION_NONE, NULL}; | |
2030 } | |
2031 | |
2032 static struct action | |
2033 insert_at_beginning(ledit_view *view, char *text, size_t len) { | |
2034 if (!key_stack_empty()) | |
2035 return err_invalid_key(view); | |
2036 enter_insert(view, text, len); | |
2037 size_t new_index = 0; | |
2038 if (!view->buffer->hard_line_based) { | |
2039 size_t tmp; | |
2040 view_get_pos_softline_bounds(view, view->cur_line, view-… | |
2041 } | |
2042 push_undo_empty_insert(view, view->cur_line, view->cur_index, 1); | |
2043 view->cur_index = new_index; | |
2044 view_wipe_line_cursor_attrs(view, view->cur_line); | |
2045 return (struct action){ACTION_NONE, NULL}; | |
2046 } | |
2047 | |
2048 static struct action | |
2049 cursor_to_first_non_ws(ledit_view *view, char *text, size_t len) { | |
2050 (void)text; | |
2051 (void)len; | |
2052 motion_callback cb; | |
2053 int num = get_key_repeat_and_motion_cb(view, &cb); | |
2054 if (num != 0) | |
2055 return err_invalid_key(view); | |
2056 size_t new_index = 0; | |
2057 if (view->buffer->hard_line_based) { | |
2058 new_index = view_line_next_non_whitespace(view, view->cu… | |
2059 } else { | |
2060 size_t start, end; | |
2061 view_get_pos_softline_bounds(view, view->cur_line, view-… | |
2062 new_index = view_line_next_non_whitespace(view, view->cu… | |
2063 /* next non-whitespace might be on next softline */ | |
2064 if (new_index >= end) { | |
2065 view_prev_cursor_pos( | |
2066 view, view->cur_line, end, 1, 0, NULL, &new_… | |
2067 ); | |
2068 } | |
2069 } | |
2070 if (cb != NULL) { | |
2071 cb(view, view->cur_line, new_index, KEY_MOTION_CHAR); | |
2072 } else { | |
2073 view->cur_index = new_index; | |
2074 if (view->mode == VISUAL) { | |
2075 view_set_selection( | |
2076 view, | |
2077 view->sel.line1, view->sel.byte1, | |
2078 view->cur_line, view->cur_index | |
2079 ); | |
2080 } else if (view->mode == NORMAL) { | |
2081 view_set_line_cursor_attrs( | |
2082 view, view->cur_line, view->cur_index | |
2083 ); | |
2084 } | |
2085 discard_repetition_stack(); | |
2086 } | |
2087 return (struct action){ACTION_NONE, NULL}; | |
2088 } | |
2089 | |
2090 static struct action | |
2091 cursor_to_beginning(ledit_view *view, char *text, size_t len) { | |
2092 (void)text; | |
2093 (void)len; | |
2094 motion_callback cb; | |
2095 int num = get_key_repeat_and_motion_cb(view, &cb); | |
2096 if (num != 0) | |
2097 return err_invalid_key(view); | |
2098 /* FIXME: should anything be done with num? */ | |
2099 size_t start_index = 0; | |
2100 if (!view->buffer->hard_line_based) { | |
2101 size_t tmp; | |
2102 view_get_pos_softline_bounds(view, view->cur_line, view-… | |
2103 } | |
2104 if (cb != NULL) { | |
2105 cb(view, view->cur_line, start_index, KEY_MOTION_CHAR); | |
2106 } else { | |
2107 view->cur_index = start_index; | |
2108 if (view->mode == VISUAL) { | |
2109 view_set_selection( | |
2110 view, | |
2111 view->sel.line1, view->sel.byte1, | |
2112 view->cur_line, view->cur_index | |
2113 ); | |
2114 } else if (view->mode == NORMAL) { | |
2115 view_set_line_cursor_attrs( | |
2116 view, view->cur_line, view->cur_index | |
2117 ); | |
2118 } | |
2119 discard_repetition_stack(); | |
2120 } | |
2121 clear_key_stack(); | |
2122 return (struct action){ACTION_NONE, NULL}; | |
2123 } | |
2124 | |
2125 static struct action | |
2126 enter_visual(ledit_view *view, char *text, size_t len) { | |
2127 (void)text; | |
2128 (void)len; | |
2129 view_set_mode(view, VISUAL); | |
2130 /* FIXME: set view->sel_valid? */ | |
2131 view->sel.line1 = view->sel.line2 = view->cur_line; | |
2132 view->sel.byte1 = view->sel.byte2 = view->cur_index; | |
2133 view_wipe_line_cursor_attrs(view, view->cur_line); | |
2134 clear_key_stack(); /* FIXME: error if not empty? */ | |
2135 return (struct action){ACTION_NONE, NULL}; | |
2136 } | |
2137 | |
2138 static struct action | |
2139 switch_selection_end(ledit_view *view, char *text, size_t len) { | |
2140 (void)text; | |
2141 (void)len; | |
2142 swap_sz(&view->sel.line1, &view->sel.line2); | |
2143 swap_sz(&view->sel.byte1, &view->sel.byte2); | |
2144 view->cur_line = view->sel.line2; | |
2145 view->cur_index = view->sel.byte2; | |
2146 return (struct action){ACTION_NONE, NULL}; | |
2147 } | |
2148 | |
2149 static struct action | |
2150 enter_commandedit(ledit_view *view, char *text, size_t len) { | |
2151 (void)text; | |
2152 (void)len; | |
2153 /* FIXME: wipe selection? */ | |
2154 char *str = view->sel_valid ? ":'<,'>" : ":"; | |
2155 window_set_bottom_bar_text(view->window, str, -1); | |
2156 window_set_bottom_bar_cursor(view->window, strlen(str)); | |
2157 window_set_bottom_bar_min_pos(view->window, 1); | |
2158 view->cur_command_type = CMD_EDIT; | |
2159 window_set_bottom_bar_text_shown(view->window, 1); | |
2160 discard_repetition_stack(); | |
2161 return (struct action){ACTION_GRABKEY, &command_key_handler}; | |
2162 } | |
2163 | |
2164 /* FIXME: support visual mode - maybe change selection to new position | |
2165 or at least support only searching within the range given by the | |
2166 selection */ | |
2167 static struct action | |
2168 enter_searchedit_forward(ledit_view *view, char *text, size_t len) { | |
2169 (void)text; | |
2170 (void)len; | |
2171 window_set_bottom_bar_text(view->window, "/", -1); | |
2172 window_set_bottom_bar_min_pos(view->window, 1); | |
2173 window_set_bottom_bar_cursor(view->window, 1); | |
2174 view->cur_command_type = CMD_EDITSEARCH; | |
2175 window_set_bottom_bar_text_shown(view->window, 1); | |
2176 discard_repetition_stack(); | |
2177 return (struct action){ACTION_GRABKEY, &command_key_handler}; | |
2178 } | |
2179 | |
2180 static struct action | |
2181 enter_searchedit_backward(ledit_view *view, char *text, size_t len) { | |
2182 (void)text; | |
2183 (void)len; | |
2184 window_set_bottom_bar_text(view->window, "?", -1); | |
2185 window_set_bottom_bar_min_pos(view->window, 1); | |
2186 window_set_bottom_bar_cursor(view->window, 1); | |
2187 view->cur_command_type = CMD_EDITSEARCHB; | |
2188 window_set_bottom_bar_text_shown(view->window, 1); | |
2189 discard_repetition_stack(); | |
2190 return (struct action){ACTION_GRABKEY, &command_key_handler}; | |
2191 } | |
2192 | |
2193 /* FIXME: differentiate between jumping to line and index like nvi */ | |
2194 static struct action | |
2195 insert_mark_cb(ledit_view *view, char *text, size_t len) { | |
2196 grab_char_cb = NULL; | |
2197 buffer_insert_mark( | |
2198 view->buffer, text, len, view->cur_line, view->cur_index | |
2199 ); | |
2200 return (struct action){ACTION_NONE, NULL}; | |
2201 } | |
2202 | |
2203 static struct action | |
2204 jump_to_mark_cb(ledit_view *view, char *text, size_t len) { | |
2205 grab_char_cb = NULL; | |
2206 motion_callback cb; | |
2207 int num = get_key_repeat_and_motion_cb(view, &cb); | |
2208 if (num > 0) | |
2209 return err_invalid_key(view); | |
2210 size_t line = 0, index = 0; | |
2211 /* FIXME: better error */ | |
2212 if (buffer_get_mark(view->buffer, text, len, &line, &index)) | |
2213 return err_invalid_key(view); | |
2214 if (view->mode == VISUAL) { | |
2215 view_set_selection( | |
2216 view, view->sel.line1, view->sel.byte1, line, index | |
2217 ); | |
2218 view->cur_line = line; | |
2219 view->cur_index = index; | |
2220 } else { | |
2221 if (cb) { | |
2222 cb(view, line, index, KEY_MOTION_LINE); | |
2223 } else { | |
2224 view_wipe_line_cursor_attrs(view, view->cur_line… | |
2225 view->cur_line = line; | |
2226 view->cur_index = index; | |
2227 if (view->mode == NORMAL) { | |
2228 view->cur_index = view_get_legal_normal_… | |
2229 view, view->cur_line, view->cur_index | |
2230 ); | |
2231 view_set_line_cursor_attrs( | |
2232 view, view->cur_line, view->cur_index | |
2233 ); | |
2234 } | |
2235 discard_repetition_stack(); | |
2236 } | |
2237 } | |
2238 return (struct action){ACTION_NONE, NULL}; | |
2239 } | |
2240 | |
2241 static struct action | |
2242 insert_mark(ledit_view *view, char *text, size_t len) { | |
2243 (void)view; | |
2244 (void)text; | |
2245 (void)len; | |
2246 grab_char_cb = &insert_mark_cb; | |
2247 discard_repetition_stack(); | |
2248 return (struct action){ACTION_NONE, NULL}; | |
2249 } | |
2250 | |
2251 static struct action | |
2252 jump_to_mark(ledit_view *view, char *text, size_t len) { | |
2253 (void)view; | |
2254 (void)text; | |
2255 (void)len; | |
2256 grab_char_cb = &jump_to_mark_cb; | |
2257 /* FIXME: should it be discarded here? */ | |
2258 discard_repetition_stack(); | |
2259 return (struct action){ACTION_NONE, NULL}; | |
2260 } | |
2261 | |
2262 /* FIXME: support visual mode, i.e. change selection to new place? */ | |
2263 static struct action | |
2264 key_search_next(ledit_view *view, char *text, size_t len) { | |
2265 (void)text; | |
2266 (void)len; | |
2267 search_next(view); | |
2268 discard_repetition_stack(); | |
2269 return (struct action){ACTION_NONE, NULL}; | |
2270 } | |
2271 | |
2272 static struct action | |
2273 key_search_prev(ledit_view *view, char *text, size_t len) { | |
2274 (void)text; | |
2275 (void)len; | |
2276 search_prev(view); | |
2277 discard_repetition_stack(); | |
2278 return (struct action){ACTION_NONE, NULL}; | |
2279 } | |
2280 | |
2281 static struct action | |
2282 show_line(ledit_view *view, char *text, size_t len) { | |
2283 (void)text; | |
2284 (void)len; | |
2285 window_show_message_fmt( | |
2286 view->window, | |
2287 "%s: %s: line %zu of %zu", | |
2288 view->buffer->filename ? view->buffer->filename : "(no filen… | |
2289 view->buffer->modified ? "modified" : "unmodified", | |
2290 add_sz(view->cur_line, 1), view->lines_num | |
2291 ); | |
2292 discard_repetition_stack(); | |
2293 return (struct action){ACTION_NONE, NULL}; | |
2294 } | |
2295 | |
2296 static struct action | |
2297 undo(ledit_view *view, char *text, size_t len) { | |
2298 (void)text; | |
2299 (void)len; | |
2300 int num = get_key_repeat(); | |
2301 if (num == -1) | |
2302 return err_invalid_key(view); | |
2303 if (num == 0) | |
2304 num = 1; | |
2305 view_wipe_selection(view); | |
2306 view_undo(view, num); | |
2307 if (view->mode != INSERT) | |
2308 finalize_repetition_stack(); | |
2309 return (struct action){ACTION_NONE, NULL}; | |
2310 } | |
2311 | |
2312 static struct action | |
2313 redo(ledit_view *view, char *text, size_t len) { | |
2314 (void)text; | |
2315 (void)len; | |
2316 int num = get_key_repeat(); | |
2317 if (num == -1) | |
2318 return err_invalid_key(view); | |
2319 if (num == 0) | |
2320 num = 1; | |
2321 view_wipe_selection(view); | |
2322 view_redo(view, num); | |
2323 if (view->mode != INSERT) | |
2324 finalize_repetition_stack(); | |
2325 return (struct action){ACTION_NONE, NULL}; | |
2326 } | |
2327 | |
2328 static struct action | |
2329 insert_mode_insert_text(ledit_view *view, char *text, size_t len) { | |
2330 if (!key_stack_empty()) | |
2331 return err_invalid_key(view); | |
2332 /* this shouldn't be necessary */ | |
2333 delete_selection(view); | |
2334 insert_text(view, view->cur_line, view->cur_index, text, len, 0,… | |
2335 return (struct action){ACTION_NONE, NULL}; | |
2336 } | |
2337 | |
2338 static struct action | |
2339 clipcopy(ledit_view *view, char *text, size_t len) { | |
2340 (void)text; | |
2341 (void)len; | |
2342 if (!key_stack_empty()) | |
2343 return err_invalid_key(view); | |
2344 /* FIXME: abstract this through view */ | |
2345 clipboard_primary_to_clipboard(view->buffer->clipboard); | |
2346 discard_repetition_stack(); | |
2347 return (struct action){ACTION_NONE, NULL}; | |
2348 } | |
2349 | |
2350 static struct action | |
2351 clippaste(ledit_view *view, char *text, size_t len) { | |
2352 (void)text; | |
2353 (void)len; | |
2354 if (!key_stack_empty()) | |
2355 return err_invalid_key(view); | |
2356 /* FIXME: the selection deletion and pasting should be in the sa… | |
2357 if (view->mode == VISUAL) { | |
2358 /* Note; this sets the current position */ | |
2359 delete_selection(view); | |
2360 } | |
2361 view_paste_clipboard(view); | |
2362 if (view->mode != INSERT) | |
2363 finalize_repetition_stack(); | |
2364 return (struct action){ACTION_NONE, NULL}; | |
2365 } | |
2366 | |
2367 /* FIXME: make sure the found position is valid cursor position? */ | |
2368 static int | |
2369 search_str_backwards(char *haystack, size_t hlen, char *needle, size_t n… | |
2370 if (start_index > hlen) | |
2371 return -1; | |
2372 size_t new_index = start_index; | |
2373 for (; new_index > 0; new_index--) { | |
2374 if (!strncmp(haystack + new_index - 1, needle, nlen)) { | |
2375 *ret = new_index - 1; | |
2376 return 0; | |
2377 } | |
2378 } | |
2379 return -1; | |
2380 } | |
2381 | |
2382 static int | |
2383 search_str_forwards(char *haystack, size_t hlen, char *needle, size_t nl… | |
2384 if (start_index >= hlen) | |
2385 return -1; | |
2386 /* duplicate so it is nul-terminated */ | |
2387 char *search_str = ledit_strndup(needle, nlen); | |
2388 char *res = strstr(haystack + start_index + 1, search_str); | |
2389 free(search_str); | |
2390 /* FIXME: is this legal? */ | |
2391 if (res) { | |
2392 *ret = (size_t)(res - haystack); | |
2393 return 0; | |
2394 } else { | |
2395 return -1; | |
2396 } | |
2397 } | |
2398 | |
2399 /* just to make the macro below work for all cases */ | |
2400 /* FIXME: is there a more elegant way to do this? */ | |
2401 static void | |
2402 dummy_cursor_helper( | |
2403 ledit_view *view, size_t line, size_t byte, | |
2404 int num, int multiline, size_t *line_ret, size_t *byte_ret) { | |
2405 (void)view; (void)num; (void)multiline; | |
2406 if (line_ret) | |
2407 *line_ret = line; | |
2408 if (byte_ret) | |
2409 *byte_ret = byte; | |
2410 } | |
2411 | |
2412 /* FIXME: add checks to functions that current mode is supported */ | |
2413 | |
2414 /* name is the name of the generated pair of functions | |
2415 search_func is used to get the next index (possibly called | |
2416 repeatedly if there is a repeat number on the key stack) | |
2417 funcm = func motion, funcn = func normal, funcv = func visual | |
2418 -> these are called to modify the index returned by search_func | |
2419 cur_funcm is called to get the index for a motion callback | |
2420 cur_funcn is called to position the cursor in normal and insert mo… | |
2421 cur_funcv is called to position the cursor in visual mode */ | |
2422 #define GEN_MOVE_TO_CHAR(name, search_func, cur_funcm, cur_funcn, cur_fu… | |
2423 static struct action … | |
2424 name##_cb(ledit_view *view, char *text, size_t len) { … | |
2425 motion_callback cb = NULL; … | |
2426 int num = get_key_repeat_and_motion_cb(view, &cb); … | |
2427 if (num == -1) … | |
2428 return err_invalid_key(view); … | |
2429 if (num == 0) … | |
2430 num = 1; … | |
2431 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); … | |
2432 size_t new_index = view->cur_index; … | |
2433 int ret = -1; … | |
2434 for (int i = 0; i < num; i++) { … | |
2435 if ((ret = search_func( … | |
2436 ll->text, ll->len, text, len, … | |
2437 new_index, &new_index)) == -1) { … | |
2438 break; … | |
2439 } … | |
2440 } … | |
2441 if (!ret) { … | |
2442 if (cb != NULL) { … | |
2443 cur_funcm( … | |
2444 view, view->cur_line, new_index, … | |
2445 1, 0, NULL, &new_index … | |
2446 ); … | |
2447 cb(view, view->cur_line, new_index, KEY_MOTION_C… | |
2448 } else { … | |
2449 if (view->mode == VISUAL) { … | |
2450 cur_funcv( … | |
2451 view, view->cur_line, new_index, … | |
2452 1, 0, NULL, &view->cur_index … | |
2453 ); … | |
2454 view_set_selection( … | |
2455 view, … | |
2456 view->sel.line1, view->sel.byte1, … | |
2457 view->cur_line, view->cur_index … | |
2458 ); … | |
2459 } else { … | |
2460 cur_funcn( … | |
2461 view, view->cur_line, new_index, … | |
2462 1, 0, NULL, &view->cur_index … | |
2463 ); … | |
2464 if (view->mode == NORMAL) { … | |
2465 view_set_line_cursor_attrs( … | |
2466 view, view->cur_line, view->… | |
2467 ); … | |
2468 } … | |
2469 } … | |
2470 discard_repetition_stack(); … | |
2471 } … | |
2472 } … | |
2473 clear_key_stack(); … | |
2474 grab_char_cb = NULL; … | |
2475 return (struct action){ACTION_NONE, NULL}; … | |
2476 } … | |
2477 … | |
2478 static struct action … | |
2479 name(ledit_view *view, char *text, size_t len) { … | |
2480 (void)view; … | |
2481 (void)text; … | |
2482 (void)len; … | |
2483 grab_char_cb = &name##_cb; … | |
2484 return (struct action){ACTION_NONE, NULL}; … | |
2485 } | |
2486 | |
2487 /* FIXME: more sensible names */ | |
2488 /* FIXME: dummy_cursor_helper is kind of ugly */ | |
2489 GEN_MOVE_TO_CHAR( | |
2490 find_next_char_forwards, search_str_forwards, | |
2491 dummy_cursor_helper, view_prev_cursor_pos, dummy_cursor_helper | |
2492 ) | |
2493 GEN_MOVE_TO_CHAR( | |
2494 find_next_char_backwards, search_str_backwards, | |
2495 view_next_cursor_pos, view_next_cursor_pos, view_next_cursor_pos | |
2496 ) | |
2497 GEN_MOVE_TO_CHAR( | |
2498 find_char_forwards, search_str_forwards, | |
2499 view_next_cursor_pos, dummy_cursor_helper, dummy_cursor_helper | |
2500 ) | |
2501 GEN_MOVE_TO_CHAR( | |
2502 find_char_backwards, search_str_backwards, | |
2503 dummy_cursor_helper, dummy_cursor_helper, dummy_cursor_helper | |
2504 ) | |
2505 | |
2506 static struct action | |
2507 loweruppercase(ledit_view *view, int upper) { | |
2508 /* FIXME: shouldn't CHECK_VIEW_LOCKED be in a lot more places? */ | |
2509 CHECK_VIEW_LOCKED(view); | |
2510 /* FIXME: move most of this to convenience functions in buffer.c… | |
2511 ledit_line *line = buffer_get_line(view->buffer, view->cur_line); | |
2512 if (view->cur_index >= line->len) | |
2513 return (struct action){ACTION_NONE, NULL}; | |
2514 buffer_normalize_line(line); | |
2515 size_t start_index = view->cur_index; | |
2516 #if ENABLE_UTF8PROC | |
2517 utf8proc_int32_t c; | |
2518 /* FIXME: this cast to utf8proc_uint8_t probably doesn't break a… | |
2519 utf8proc_ssize_t origlen = utf8proc_iterate((utf8proc_uint8_t *)… | |
2520 /* FIXME: show error message? */ | |
2521 if (c < 0 || origlen < 1) | |
2522 return (struct action){ACTION_NONE, NULL}; | |
2523 utf8proc_int32_t u; | |
2524 if (upper) | |
2525 u = utf8proc_toupper(c); | |
2526 else | |
2527 u = utf8proc_tolower(c); | |
2528 utf8proc_uint8_t u8[4]; | |
2529 utf8proc_ssize_t newlen = utf8proc_encode_char(u, u8); | |
2530 if (newlen < 1) | |
2531 return (struct action){ACTION_NONE, NULL}; | |
2532 delete_range( | |
2533 view, 0, 0, | |
2534 view->cur_line, start_index, view->cur_line, start_index + o… | |
2535 ); | |
2536 insert_text( | |
2537 view, view->cur_line, start_index, (char *)u8, newlen, | |
2538 view->cur_line, start_index, view->cur_line, start_index, 1,… | |
2539 ); | |
2540 #else | |
2541 char c; | |
2542 if (upper) | |
2543 c = toupper((unsigned char)line->text[view->cur_index]); | |
2544 else | |
2545 c = tolower((unsigned char)line->text[view->cur_index]); | |
2546 delete_range( | |
2547 view, 0, 0, | |
2548 view->cur_line, start_index, view->cur_line, start_index + 1… | |
2549 ); | |
2550 insert_text( | |
2551 view, view->cur_line, start_index, &c, 1, | |
2552 view->cur_line, start_index, view->cur_line, start_index, 1,… | |
2553 ); | |
2554 #endif | |
2555 /* If the last character on a line is replaced, the cursor would… | |
2556 backwards due to the deletion, so it has to be set to the ori… | |
2557 position again */ | |
2558 view->cur_index = start_index; | |
2559 push_undo_empty_insert(view, view->cur_line, view->cur_index, 0); | |
2560 if (view->mode == NORMAL) | |
2561 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
2562 finalize_repetition_stack(); | |
2563 return (struct action){ACTION_NONE, NULL}; | |
2564 } | |
2565 | |
2566 static struct action | |
2567 uppercase(ledit_view *view, char *text, size_t len) { | |
2568 (void)text; | |
2569 (void)len; | |
2570 return loweruppercase(view, 1); | |
2571 } | |
2572 | |
2573 static struct action | |
2574 lowercase(ledit_view *view, char *text, size_t len) { | |
2575 (void)text; | |
2576 (void)len; | |
2577 return loweruppercase(view, 0); | |
2578 } | |
2579 | |
2580 static struct action | |
2581 replace_cb(ledit_view *view, char *text, size_t len) { | |
2582 CHECK_VIEW_LOCKED(view); | |
2583 size_t start_index = view->cur_index; | |
2584 /* FIXME: replace with (key repeat) * text instead of just text … | |
2585 /* FIXME: cursor pos or char? */ | |
2586 size_t end_index; | |
2587 view_next_cursor_pos( | |
2588 view, view->cur_line, view->cur_index, 1, 0, NULL, &end_index | |
2589 ); | |
2590 delete_range( | |
2591 view, 0, 0, | |
2592 view->cur_line, start_index, view->cur_line, end_index, 0 | |
2593 ); | |
2594 insert_text( | |
2595 view, view->cur_line, start_index, text, len, | |
2596 view->cur_line, start_index, view->cur_line, start_index, 1,… | |
2597 ); | |
2598 /* If the last character on a line is replaced, the cursor would… | |
2599 backwards due to the deletion, so it has to be set to the ori… | |
2600 position again */ | |
2601 view->cur_index = start_index; | |
2602 push_undo_empty_insert(view, view->cur_line, view->cur_index, 0); | |
2603 if (view->mode == NORMAL) | |
2604 view_set_line_cursor_attrs(view, view->cur_line, view->c… | |
2605 grab_char_cb = NULL; | |
2606 finalize_repetition_stack(); | |
2607 return (struct action){ACTION_NONE, NULL}; | |
2608 } | |
2609 | |
2610 static struct action | |
2611 replace(ledit_view *view, char *text, size_t len) { | |
2612 (void)view; | |
2613 (void)text; | |
2614 (void)len; | |
2615 int num = get_key_repeat(); | |
2616 if (num != 0) | |
2617 return err_invalid_key(view); | |
2618 grab_char_cb = &replace_cb; | |
2619 return (struct action){ACTION_NONE, NULL}; | |
2620 } | |
2621 | |
2622 static struct action | |
2623 toggle_hard_line_based(ledit_view *view, char *text, size_t len) { | |
2624 (void)view; | |
2625 (void)text; | |
2626 (void)len; | |
2627 int num = get_key_repeat(); | |
2628 if (num != 0) | |
2629 return err_invalid_key(view); | |
2630 buffer_set_hard_line_based(view->buffer, !view->buffer->hard_lin… | |
2631 discard_repetition_stack(); | |
2632 return (struct action){ACTION_NONE, NULL}; | |
2633 } | |
2634 | |
2635 static struct action | |
2636 handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, uns… | |
2637 basic_key_array *cur_keys = config_get_basic_keys(lang_index); | |
2638 size_t num_keys = cur_keys->num_keys; | |
2639 /* FIXME: check if control chars in text */ | |
2640 /* FIXME: this is a bit of a hack because it's hardcoded */ | |
2641 if (grab_char_cb && sym == XK_Escape) { | |
2642 grab_char_cb = NULL; | |
2643 return (struct action){ACTION_NONE, NULL}; | |
2644 } else if (len > 0 && grab_char_cb) { | |
2645 *found = 1; | |
2646 *flags = 0; | |
2647 return grab_char_cb(view, key_text, len); | |
2648 } | |
2649 *found = 0; | |
2650 for (size_t i = 0; i < num_keys; i++) { | |
2651 if (cur_keys->keys[i].text) { | |
2652 if (len > 0 && | |
2653 (cur_keys->keys[i].modes & view->mode) && | |
2654 ((!strncmp(cur_keys->keys[i].text, key_text… | |
2655 match_key(cur_keys->keys[i].mods, key_sta… | |
2656 cur_keys->keys[i].text[0] == '\0')) { | |
2657 /* FIXME: seems a bit hacky to remove sh… | |
2658 is needed to make keys that use shift… | |
2659 *flags = cur_keys->keys[i].cb->flags; | |
2660 *found = 1; | |
2661 if (!(*flags & KEY_FLAG_LOCK_ALLOWED) &&… | |
2662 return view_locked_error(view); | |
2663 return cur_keys->keys[i].cb->func(view, … | |
2664 } | |
2665 } else if ((cur_keys->keys[i].modes & view->mode) && | |
2666 cur_keys->keys[i].keysym == sym && | |
2667 match_key(cur_keys->keys[i].mods, key_state))… | |
2668 *flags = cur_keys->keys[i].cb->flags; | |
2669 *found = 1; | |
2670 if (!(*flags & KEY_FLAG_LOCK_ALLOWED) && view->l… | |
2671 return view_locked_error(view); | |
2672 return cur_keys->keys[i].cb->func(view, key_text… | |
2673 } | |
2674 } | |
2675 return (struct action){ACTION_NONE, NULL}; | |
2676 } | |
2677 | |
2678 static struct action | |
2679 repeat_command(ledit_view *view, char *text, size_t len) { | |
2680 (void)view; | |
2681 (void)text; | |
2682 (void)len; | |
2683 int num = get_key_repeat(); | |
2684 if (num == -1) | |
2685 return err_invalid_key(view); | |
2686 if (num == 0) | |
2687 num = 1; | |
2688 if (repetition_stack.len == 0) { | |
2689 window_show_message(view->window, "No previous command",… | |
2690 discard_repetition_stack(); | |
2691 return (struct action){ACTION_NONE, NULL}; | |
2692 } | |
2693 int found; | |
2694 basic_key_cb_flags flags; | |
2695 repetition_stack.replaying = 1; | |
2696 for (int i = 0; i < num; i++) { | |
2697 unwind_repetition_stack(); | |
2698 struct repetition_stack_elem *e = get_cur_repetition_sta… | |
2699 while (e) { | |
2700 (void)handle_key(view, e->key_text, e->len, e->s… | |
2701 advance_repetition_stack(); | |
2702 e = get_cur_repetition_stack_elem(); | |
2703 } | |
2704 } | |
2705 repetition_stack.replaying = 0; | |
2706 discard_repetition_stack(); | |
2707 clear_key_stack(); | |
2708 return (struct action){ACTION_NONE, NULL}; | |
2709 } | |
2710 | |
2711 struct action | |
2712 basic_key_handler(ledit_view *view, unsigned int key_state, KeySym sym, … | |
2713 struct repetition_stack_elem *re = push_repetition_stack(); | |
2714 re->key_text = ledit_strndup(buf, (size_t)n); | |
2715 re->len = (size_t)n; | |
2716 re->sym = sym; | |
2717 re->key_state = key_state; | |
2718 re->lang_index = lang_index; | |
2719 | |
2720 /* FIXME: figure out when to actually hide message and | |
2721 ensure cursor shown */ | |
2722 /* FIXME: clean up interface (what has a setter method and what … | |
2723 int found = 0; | |
2724 int msg_shown = view->window->message_shown; | |
2725 view->window->message_shown = 0; /* FIXME: this is hacky */ | |
2726 basic_key_cb_flags flags; | |
2727 struct action act = handle_key(view, buf, (size_t)n, sym, key_st… | |
2728 /* FIXME: make message hiding a property of each command so e.g.… | |
2729 if (found && n > 0 && !view->window->message_shown) | |
2730 window_hide_message(view->window); | |
2731 else if (msg_shown) | |
2732 view->window->message_shown = msg_shown; | |
2733 | |
2734 if (found && (flags & KEY_FLAG_JUMP_TO_CURSOR)) | |
2735 view_ensure_cursor_shown(view); | |
2736 /* FIXME: this also doesn't show real invalid keys in insert mode | |
2737 -> it needs to be this way to avoid showing anything on modif… | |
2738 keys, but maybe it should just check explicitly for modifier … | |
2739 if (!found && n > 0) { | |
2740 window_show_message(view->window, "Invalid key", -1); | |
2741 discard_repetition_stack(); | |
2742 clear_key_stack(); | |
2743 } | |
2744 return act; | |
2745 } |