Introduction
Introduction Statistics Contact Development Disclaimer Help
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 }
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.