Introduction
Introduction Statistics Contact Development Disclaimer Help
keys_command.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_command.c (33463B)
---
1 /* FIXME: remove CHECK_VIEW_LOCKED when it is confirmed that the new sys…
2 /* FIXME: Parse commands properly and allow combinations of commands */
3 /* FIXME: properly parse commands - in particular, shown an error if the…
4 characters on the line */
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <unistd.h>
10
11 #include <X11/Xlib.h>
12 #include <X11/Xutil.h>
13 #include <pango/pangoxft.h>
14 #include <X11/extensions/Xdbe.h>
15 #include <X11/keysym.h>
16 #include <X11/XF86keysym.h>
17 #include <X11/cursorfont.h>
18
19 #include "memory.h"
20 #include "common.h"
21 #include "txtbuf.h"
22 #include "undo.h"
23 #include "cache.h"
24 #include "window.h"
25 #include "buffer.h"
26 #include "view.h"
27 #include "search.h"
28 #include "cleanup.h"
29 #include "util.h"
30
31 #include "keys.h"
32 #include "keys_command.h"
33 #include "configparser.h"
34
35 /***********************************************************************…
36 * Declarations for all functions that can be used in the configuration.…
37 ***********************************************************************…
38
39 static int substitute_yes(ledit_view *view, char *key_text, size_t len, …
40 static int substitute_yes_all(ledit_view *view, char *key_text, size_t l…
41 static int substitute_no(ledit_view *view, char *key_text, size_t len, s…
42 static int substitute_no_all(ledit_view *view, char *key_text, size_t le…
43 static int edit_insert_text(ledit_view *view, char *key_text, size_t len…
44 static int edit_cursor_left(ledit_view *view, char *key_text, size_t len…
45 static int edit_cursor_right(ledit_view *view, char *key_text, size_t le…
46 static int edit_cursor_to_end(ledit_view *view, char *key_text, size_t l…
47 static int edit_cursor_to_beginning(ledit_view *view, char *key_text, si…
48 static int edit_backspace(ledit_view *view, char *key_text, size_t len, …
49 static int edit_delete(ledit_view *view, char *key_text, size_t len, siz…
50 static int edit_submit(ledit_view *view, char *key_text, size_t len, siz…
51 static int edit_prevcommand(ledit_view *view, char *key_text, size_t len…
52 static int edit_nextcommand(ledit_view *view, char *key_text, size_t len…
53 static int edit_prevsearch(ledit_view *view, char *key_text, size_t len,…
54 static int edit_nextsearch(ledit_view *view, char *key_text, size_t len,…
55 static int editsearch_submit(ledit_view *view, char *key_text, size_t le…
56 static int editsearchb_submit(ledit_view *view, char *key_text, size_t l…
57 static int edit_discard(ledit_view *view, char *key_text, size_t len, si…
58
59 static int create_view(ledit_view *view, char *cmd, size_t l1, size_t l2…
60 static int close_view(ledit_view *view, char *cmd, size_t l1, size_t l2);
61 static int handle_write(ledit_view *view, char *cmd, size_t l1, size_t l…
62 static int handle_quit(ledit_view *view, char *cmd, size_t l1, size_t l2…
63 static int handle_write_quit(ledit_view *view, char *cmd, size_t l1, siz…
64 static int handle_substitute(ledit_view *view, char *cmd, size_t l1, siz…
65
66 /***********************************************
67 * String-function mapping for config parsing. *
68 ***********************************************/
69
70 typedef enum {
71 KEY_FLAG_NONE = 0,
72 KEY_FLAG_JUMP_TO_CURSOR = 1,
73 KEY_FLAG_LOCK_ALLOWED = 2
74 } command_key_cb_flags;
75
76 typedef enum {
77 CMD_FLAG_NONE = 0,
78 CMD_FLAG_OPTIONAL_RANGE = 1,
79 CMD_FLAG_LOCK_ALLOWED = 2
80 } command_cb_flags;
81
82 typedef int (*command_key_cb_func)(ledit_view *, char *, size_t, size_t);
83 struct command_key_cb {
84 char *text;
85 command_key_cb_func func;
86 command_key_cb_flags flags;
87 command_mode allowed_modes;
88 };
89
90 typedef int (*command_cb_func)(ledit_view *view, char *cmd, size_t l1, s…
91 struct command_cb {
92 char *text;
93 command_cb_func func;
94 command_cb_flags flags;
95 };
96
97 int
98 command_key_cb_modemask_is_valid(command_key_cb *cb, command_mode modes)…
99 return (~cb->allowed_modes & modes) == 0;
100 }
101
102 static command_key_cb command_key_cb_map[] = {
103 {"edit-backspace", &edit_backspace, KEY_FLAG_LOCK_ALLOWED, CMD_E…
104 {"edit-cursor-left", &edit_cursor_left, KEY_FLAG_LOCK_ALLOWED, C…
105 {"edit-cursor-right", &edit_cursor_right, KEY_FLAG_LOCK_ALLOWED,…
106 {"edit-cursor-to-beginning", &edit_cursor_to_beginning, KEY_FLAG…
107 {"edit-cursor-to-end", &edit_cursor_to_end, KEY_FLAG_LOCK_ALLOWE…
108 {"edit-delete", &edit_delete, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CM…
109 {"edit-discard", &edit_discard, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|…
110 {"edit-insert-text", &edit_insert_text, KEY_FLAG_LOCK_ALLOWED, C…
111 {"edit-next-command", &edit_nextcommand, KEY_FLAG_LOCK_ALLOWED, …
112 {"edit-next-search", &edit_nextsearch, KEY_FLAG_LOCK_ALLOWED, CM…
113 {"edit-previous-command", &edit_prevcommand, KEY_FLAG_LOCK_ALLOW…
114 {"edit-previous-search", &edit_prevsearch, KEY_FLAG_LOCK_ALLOWED…
115 {"edit-submit", &edit_submit, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT},
116 {"edit-submit-backwards-search", &editsearchb_submit, KEY_FLAG_L…
117 {"edit-submit-search", &editsearch_submit, KEY_FLAG_LOCK_ALLOWED…
118 {"substitute-no", &substitute_no, KEY_FLAG_JUMP_TO_CURSOR|KEY_FL…
119 {"substitute-no-all", &substitute_no_all, KEY_FLAG_JUMP_TO_CURSO…
120 {"substitute-yes", &substitute_yes, KEY_FLAG_JUMP_TO_CURSOR, CMD…
121 {"substitute-yes-all", &substitute_yes_all, KEY_FLAG_JUMP_TO_CUR…
122 };
123
124 static command_cb command_cb_map[] = {
125 {"close-view", &close_view, CMD_FLAG_LOCK_ALLOWED},
126 {"create-view", &create_view, CMD_FLAG_LOCK_ALLOWED},
127 {"quit", &handle_quit, CMD_FLAG_LOCK_ALLOWED},
128 {"substitute", &handle_substitute, CMD_FLAG_OPTIONAL_RANGE},
129 {"write", &handle_write, CMD_FLAG_OPTIONAL_RANGE|CMD_FLAG_LOCK_A…
130 {"write-quit", &handle_write_quit, CMD_FLAG_OPTIONAL_RANGE|CMD_F…
131 };
132
133 GEN_CB_MAP_HELPERS(command_key_cb_map, command_key_cb, text)
134 GEN_CB_MAP_HELPERS(command_cb_map, command_cb, text)
135
136 /***************************************************
137 * General global variables and utility functions. *
138 ***************************************************/
139
140 static struct {
141 char *search;
142 char *replace;
143 size_t slen;
144 size_t rlen;
145 size_t line;
146 size_t byte;
147 size_t old_line;
148 size_t old_byte;
149 size_t max_line;
150 int global;
151 int num;
152 int start_group; /* only set for the first replacement */
153 } sub_state = {NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
154
155 typedef struct {
156 size_t len, cur, cap;
157 char **cmds;
158 } history;
159
160 history cmdhistory = {0, 0, 0, NULL};
161
162 history searchhistory = {0, 0, 0, NULL};
163
164 static void
165 push_history(history *hist, char *cmd, size_t len) {
166 if (hist->len >= hist->cap) {
167 size_t cap = ideal_array_size(hist->cap, add_sz(hist->ca…
168 hist->cmds = ledit_reallocarray(hist->cmds, cap, sizeof(…
169 hist->cap = cap;
170 }
171 hist->cmds[hist->len] = ledit_strndup(cmd, len);
172 hist->len++;
173 hist->cur = hist->len;
174 }
175
176 static void
177 push_cmdhistory(char *cmd, size_t len) {
178 push_history(&cmdhistory, cmd, len);
179 }
180
181 static void
182 push_searchhistory(char *search, size_t len) {
183 push_history(&searchhistory, search, len);
184 }
185
186 void
187 command_key_cleanup(void) {
188 free(sub_state.search);
189 free(sub_state.replace);
190 for (size_t i = 0; i < cmdhistory.len; i++) {
191 free(cmdhistory.cmds[i]);
192 }
193 for (size_t i = 0; i < searchhistory.len; i++) {
194 free(searchhistory.cmds[i]);
195 }
196 free(cmdhistory.cmds);
197 free(searchhistory.cmds);
198 }
199
200 static int
201 view_locked_error(ledit_view *view) {
202 window_show_message(view->window, view->lock_text, -1);
203 return 0;
204 }
205
206 #define CHECK_VIEW_LOCKED(view) if (view->lock_text) return view_locked_…
207
208 /********************************************************************
209 * Functions for handling commands typed in line editor (:w, etc.). *
210 ********************************************************************/
211
212 static int parse_range(
213 ledit_view *view, char *cmd, size_t len, size_t *idx_ret,
214 size_t *line1_ret, size_t *line2_ret, int *l1_valid, int *l2_valid,
215 char **errstr_ret
216 );
217 static int handle_cmd(ledit_view *view, char *cmd, size_t len, size_t la…
218
219 /* FIXME: USE LEN EVERYWHERE INSTEAD OF RELYING ON cmd BEING NUL-TERMINA…
220 /* returns 1 on error, 0 otherwise */
221 static int
222 handle_write_base(ledit_view *view, char *cmd) {
223 #if TEST
224 /* disallow normal file writing in test mode so no
225 file can accidentally be destroyed by fuzz testing */
226 (void)view;
227 (void)cmd;
228 return 0;
229 #else
230 /* FIXME: allow writing only part of file */
231 char *filename = view->buffer->filename;
232 int stored = 1;
233 int force = 0;
234 if (*cmd == '!') {
235 force = 1;
236 cmd++;
237 }
238 /* FIXME: string parsing instead of just taking the rest of the …
239 if (cmd[0] == ' ' && cmd[1] != '\0') {
240 filename = cmd + 1;
241 stored = 0;
242 }
243 /* FIXME: file locks */
244 char *errstr = NULL;
245 if (filename) {
246 struct stat sb;
247 /* There technically is a race between checking stat and…
248 trying to write the file, but I don't care at the mom…
249 int ret = 0;
250 if (!(ret = stat(filename, &sb)) && !force && stored &&
251 (sb.st_mtim.tv_sec != view->buffer->file_mtime.tv_se…
252 sb.st_mtim.tv_nsec != view->buffer->file_mtime.tv_n…
253 window_show_message_fmt(
254 view->window,
255 "%s: file modification time changed; use ! t…
256 filename
257 );
258 return 1;
259 /* FIXME: I guess the file can still exist if stat retur…
260 but the writing itself will probably fail then as wel…
261 } else if (!ret && !force && !stored) {
262 window_show_message_fmt(
263 view->window,
264 "%s: file exists; use ! to override",
265 filename
266 );
267 return 1;
268 } else if (buffer_write_to_filename(view->buffer, filena…
269 window_show_message_fmt(view->window, "Error wri…
270 return 1;
271 } else {
272 /* FIXME: better message */
273 window_show_message_fmt(view->window, "Wrote fil…
274 /* update modification time */
275 if (stat(filename, &sb)) {
276 /* FIXME: what should be done here? */
277 } else {
278 view->buffer->file_mtime = sb.st_mtim;
279 }
280 }
281 } else {
282 window_show_message(view->window, "No file name", -1);
283 return 1;
284 }
285 return 0;
286 #endif
287 }
288
289 static int
290 handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2) {
291 (void)l1;
292 (void)l2;
293 handle_write_base(view, cmd);
294 return 0;
295 }
296
297 static int
298 handle_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) {
299 (void)l1;
300 (void)l2;
301 int force = 0;
302 if (*cmd == '!')
303 force = 1;
304 if (view->buffer->modified && !force) {
305 window_show_message(view->window, "File modified; write …
306 } else {
307 ledit_cleanup();
308 exit(0);
309 }
310 return 0;
311 }
312
313 static int
314 create_view(ledit_view *view, char *cmd, size_t l1, size_t l2) {
315 (void)cmd;
316 (void)l1;
317 (void)l2;
318 buffer_add_view(view->buffer, view->mode, view->cur_line, view->…
319 return 0;
320 }
321
322 static int
323 close_view(ledit_view *view, char *cmd, size_t l1, size_t l2) {
324 (void)cmd;
325 (void)l1;
326 (void)l2;
327 /* FIXME: This will lead to problems if I add something that
328 requires access to the view after the command is handled. */
329 int force = 0;
330 if (*cmd == '!')
331 force = 1;
332 ledit_buffer *buffer = view->buffer;
333 if (buffer->views_num == 1 && buffer->modified && !force) {
334 window_show_message(view->window, "File modified; write …
335 } else {
336 view->destroy = 1;
337 }
338 return 0;
339 }
340
341 static int
342 handle_write_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) {
343 (void)l1;
344 (void)l2;
345 if (handle_write_base(view, cmd))
346 return 0;
347 ledit_cleanup();
348 exit(0);
349 return 0;
350 }
351
352 static void
353 show_num_substituted(ledit_view *view) {
354 window_show_message_fmt(view->window, "%d substitution(s)", sub_…
355 }
356
357 /* returns 1 when match was found, 0 otherwise */
358 static int
359 next_replace_pos(
360 ledit_view *view,
361 size_t line, size_t byte, size_t max_line,
362 size_t *line_ret, size_t *byte_ret) {
363 size_t start_index = byte;
364 for (size_t i = line; i <= max_line; i++) {
365 ledit_line *ll = buffer_get_line(view->buffer, i);
366 buffer_normalize_line(ll);
367 char *pos = strstr(ll->text + start_index, sub_state.sea…
368 if (pos != NULL) {
369 *line_ret = i;
370 *byte_ret = (size_t)(pos - ll->text);
371 return 1;
372 }
373 start_index = 0;
374 }
375 return 0;
376 }
377
378 /* returns whether keys should continue being captured */
379 static int
380 move_to_next_substitution(ledit_view *view) {
381 ledit_theme *theme = config_get_theme();
382 if (view->mode == NORMAL)
383 view_wipe_line_cursor_attrs(view, view->cur_line);
384 else if (view->mode == VISUAL)
385 view_wipe_selection(view);
386 if (!next_replace_pos(view, sub_state.line, sub_state.byte, sub_…
387 /* FIXME: why are these set here? */
388 view->cur_line = sub_state.line;
389 view->cur_index = sub_state.byte;
390 if (view->mode == NORMAL) {
391 view->cur_index = view_get_legal_normal_pos(view…
392 view_set_line_cursor_attrs(view, view->cur_line,…
393 } else if (view->mode == VISUAL) {
394 view_set_selection(view, view->cur_line, view->c…
395 }
396 window_show_message(view->window, "No more matches", -1);
397 buffer_unlock_all_views(view->buffer);
398 return 0;
399 }
400 if (theme->highlight_search && view->mode != VISUAL) {
401 view_wipe_line_cursor_attrs(view, view->cur_line);
402 view_set_mode(view, VISUAL);
403 }
404 view->cur_line = sub_state.line;
405 view->cur_index = sub_state.byte;
406 if (view->mode == NORMAL) {
407 view_set_line_cursor_attrs(view, view->cur_line, view->c…
408 } else if (view->mode == VISUAL && theme->highlight_search) {
409 view_set_selection(view, view->cur_line, view->cur_index…
410 }
411 window_show_message(view->window, "Replace? (y/Y/n/N)", -1);
412 view_ensure_cursor_shown(view);
413 return 1;
414 }
415
416 /* WARNING: sub_state must be set properly! */
417 static void
418 substitute_single(ledit_view *view) {
419 ledit_range cur_range;
420 cur_range.line1 = sub_state.old_line;
421 cur_range.byte1 = sub_state.old_byte;
422 cur_range.line2 = sub_state.line;
423 cur_range.byte2 = sub_state.byte;
424 buffer_delete_with_undo_base(
425 view->buffer, cur_range,
426 sub_state.start_group, view->mode,
427 sub_state.line, sub_state.byte,
428 sub_state.line, sub_state.byte + sub_state.slen, NULL
429 );
430 sub_state.start_group = 0;
431 cur_range.line1 = sub_state.line;
432 cur_range.byte1 = sub_state.byte;
433 buffer_insert_with_undo_base(
434 view->buffer, cur_range, 0, 0, view->mode,
435 sub_state.line, sub_state.byte,
436 sub_state.replace, sub_state.rlen,
437 NULL, NULL
438 );
439 sub_state.num++;
440 }
441
442 static void
443 substitute_all_remaining(ledit_view *view) {
444 if (view->mode == NORMAL)
445 view_wipe_line_cursor_attrs(view, view->cur_line);
446 else if (view->mode == VISUAL)
447 view_wipe_selection(view);
448 size_t min_line = SIZE_MAX;
449 while (next_replace_pos(view, sub_state.line, sub_state.byte, su…
450 if (sub_state.line < min_line)
451 min_line = sub_state.line;
452 substitute_single(view);
453 view->cur_line = sub_state.old_line = sub_state.line;
454 view->cur_index = sub_state.old_byte = sub_state.byte;
455 if (!sub_state.global) {
456 sub_state.line++;
457 sub_state.byte = 0;
458 } else {
459 sub_state.byte += sub_state.rlen;
460 }
461 }
462 if (min_line < view->lines_num)
463 buffer_recalc_all_views_from_line(view->buffer, min_line…
464 window_show_message_fmt(view->window, "Replaced %d occurrence(s)…
465 if (view->mode == NORMAL) {
466 /* this doesn't need to be added to the undo stack since…
467 view->cur_index = view_get_legal_normal_pos(view, view->…
468 view_set_line_cursor_attrs(view, view->cur_line, view->c…
469 } else if (view->mode == VISUAL) {
470 view_set_selection(view, view->cur_line, view->cur_index…
471 }
472 view_ensure_cursor_shown(view);
473 buffer_unlock_all_views(view->buffer);
474 }
475
476 static int
477 handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) {
478 CHECK_VIEW_LOCKED(view);
479 size_t len = strlen(cmd);
480 char *sep = NULL;
481 if (len == 0) goto error;
482 char *sepend = next_utf8(cmd + 1);
483 size_t seplen = sepend - cmd;
484 sep = ledit_strndup(cmd, seplen);
485 cmd += seplen;
486 char *next = strstr(cmd, sep);
487 if (next == NULL) goto error;
488 *next = '\0';
489 next += seplen;
490 char *last = strstr(next, sep);
491 if (last == NULL) goto error;
492 *last = '\0';
493 last += seplen;
494 int confirm = 0, global = 0;
495 char *c = last;
496 while (*c != '\0') {
497 switch (*c) {
498 case 'c':
499 confirm = 1;
500 break;
501 case 'g':
502 global = 1;
503 break;
504 default:
505 goto error;
506 }
507 c++;
508 }
509 free(sep);
510 sep = NULL;
511 free(sub_state.search);
512 free(sub_state.replace);
513 sub_state.search = ledit_strdup(cmd);
514 sub_state.replace = ledit_strdup(next);
515 sub_state.slen = strlen(sub_state.search);
516 sub_state.rlen = strlen(sub_state.replace);
517 sub_state.global = global;
518 sub_state.line = l1;
519 sub_state.byte = 0;
520 sub_state.old_line = view->cur_line;
521 sub_state.old_byte = view->cur_index;
522 sub_state.max_line = l2;
523 sub_state.num = 0;
524 sub_state.start_group = 1;
525
526 if (confirm) {
527 buffer_lock_all_views_except(view->buffer, view, "Ongoin…
528 view->cur_command_type = CMD_SUBSTITUTE;
529 return move_to_next_substitution(view);
530 } else {
531 substitute_all_remaining(view);
532 }
533 return 0;
534 error:
535 window_show_message(view->window, "Invalid command", -1);
536 free(sep);
537 return 0;
538 }
539
540 enum cmd_type {
541 CMD_NORMAL,
542 CMD_OPTIONAL_RANGE
543 };
544
545 /*
546 . current line
547 $ last line
548 % all lines
549 */
550
551 /* NOTE: Marks are only recognized here if they are one unicode characte…
552 /* NOTE: Only the line range of the selection is used at the moment. */
553 static int
554 parse_range(
555 ledit_view *view, char *cmd, size_t len, size_t *idx_ret,
556 size_t *line1_ret, size_t *line2_ret, int *l1_valid, int *l2_valid,
557 char **errstr_ret) {
558 *errstr_ret = "";
559 enum {
560 START_LINENO = 1,
561 START_RANGE = 2,
562 IN_RANGE = 4,
563 IN_LINENO = 8
564 } s = START_LINENO | START_RANGE;
565 size_t l1 = 0, l2 = 0;
566 *l1_valid = 0;
567 *l2_valid = 0;
568 size_t cur = 0;
569 char *c;
570 while (cur < len) {
571 c = &cmd[cur];
572 if (isdigit(*c)) {
573 if (s & IN_LINENO) {
574 size_t *final = &l2;
575 if (!*l2_valid) {
576 final = &l1;
577 *l1_valid = 1;
578 }
579 if (SIZE_MAX / 10 < *final) {
580 *errstr_ret = "Integer overflow …
581 return 1;
582 }
583 *final *= 10;
584 if (SIZE_MAX - (*c - '0') < *final) {
585 *errstr_ret = "Integer overflow …
586 return 1;
587 }
588 *final += (*c - '0');
589 } else if ((s & START_LINENO) && (s & START_RANG…
590 l1 = *c - '0';
591 *l1_valid = 1;
592 s = IN_RANGE | IN_LINENO;
593 } else if ((s & START_LINENO)) {
594 l2 = *c - '0';
595 *l2_valid = 1;
596 s = IN_LINENO;
597 }
598 } else if (*c == '\'' && (s & START_LINENO)) {
599 if (len - cur <= 2) {
600 *errstr_ret = "Invalid range";
601 return 1;
602 }
603 size_t aftermark_idx = cur + 2 + next_utf8_len(c…
604 size_t marklen = aftermark_idx - (cur + 1);
605 size_t l, b;
606 if (*(c + 1) == '<' && view->sel_valid) {
607 l = view->sel.line1 < view->sel.line2 ? …
608 } else if (*(c + 1) == '>' && view->sel_valid) {
609 l = view->sel.line1 > view->sel.line2 ? …
610 } else if (buffer_get_mark(view->buffer, c + 1, …
611 *errstr_ret = "Invalid mark";
612 return 1;
613 }
614 if (!*l1_valid) {
615 l1 = l + 1;
616 *l1_valid = 1;
617 } else {
618 l2 = l + 1;
619 *l2_valid = 1;
620 }
621 cur = aftermark_idx;
622 s = 0;
623 continue;
624 } else if (*c == ',' && !(s & START_RANGE)) {
625 if (*l1_valid && *l2_valid) {
626 *errstr_ret = "Invalid range";
627 return 1;
628 } else {
629 s = START_LINENO;
630 }
631 } else if (*c == '%') {
632 if (s & START_RANGE) {
633 l1 = 1;
634 l2 = view->lines_num;
635 *l1_valid = *l2_valid = 1;
636 cur++;
637 s = 0;
638 break;
639 } else {
640 *errstr_ret = "Invalid range";
641 return 1;
642 }
643 } else if (*c == '.') {
644 if (s & START_LINENO) {
645 if (!*l1_valid) {
646 l1 = view->cur_line + 1;
647 *l1_valid = 1;
648 } else {
649 l2 = view->cur_line + 1;
650 *l2_valid = 1;
651 }
652 s = 0;
653 } else {
654 *errstr_ret = "Invalid range";
655 return 1;
656 }
657 } else if (*c == '$') {
658 if (s & START_LINENO) {
659 if (!*l1_valid) {
660 l1 = view->lines_num;
661 *l1_valid = 1;
662 } else {
663 l2 = view->lines_num;
664 *l2_valid = 1;
665 }
666 s = 0;
667 } else {
668 *errstr_ret = "Invalid range";
669 return 1;
670 }
671 } else {
672 break;
673 }
674 cur++;
675 }
676 if ((!*l1_valid || !*l2_valid) && !(s & START_RANGE)) {
677 *errstr_ret = "Invalid range";
678 return 1;
679 }
680 if ((*l1_valid || *l2_valid) && (l1 == 0 || l2 == 0 || l1 > view…
681 *errstr_ret = "Invalid line number in range";
682 return 1;
683 }
684 *idx_ret = cur;
685 /* ranges are given 1-indexed by user */
686 *line1_ret = l1 - 1;
687 *line2_ret = l2 - 1;
688 return 0;
689 }
690
691 static int
692 handle_cmd(ledit_view *view, char *cmd, size_t len, size_t lang_index) {
693 if (len < 1)
694 return 0;
695 push_cmdhistory(cmd, len);
696 size_t l1, l2;
697 int l1_valid, l2_valid;
698 char *errstr;
699 size_t start_idx;
700 if (parse_range(view, cmd, len, &start_idx, &l1, &l2, &l1_valid,…
701 window_show_message(view->window, errstr, -1);
702 return 0;
703 }
704 if (start_idx >= len) {
705 window_show_message(view->window, "Invalid command", -1);
706 return 0;
707 }
708 size_t rem_len = len - start_idx;
709 char *cur_str = cmd + start_idx;
710 int range_given = l1_valid && l2_valid;
711 if (!range_given) {
712 l1 = l2 = view->cur_line;
713 }
714 command_array *cur_cmds = config_get_commands(lang_index);
715 char *cmd_text;
716 size_t text_len;
717 for (size_t i = 0; i < cur_cmds->num_cmds; i++) {
718 cmd_text = cur_cmds->cmds[i].text;
719 text_len = strlen(cmd_text);
720 if (rem_len < text_len)
721 continue;
722 if (!strncmp(cmd_text, cur_str, text_len)) {
723 if (range_given && !(cur_cmds->cmds[i].cb->flags…
724 window_show_message(view->window, "Comma…
725 return 0;
726 } else if (view->lock_text && !(cur_cmds->cmds[i…
727 window_show_message(view->window, view->…
728 return 0;
729 }
730 return cur_cmds->cmds[i].cb->func(view, cur_str …
731 }
732 }
733 window_show_message(view->window, "Invalid command", -1);
734 return 0;
735 }
736
737 /***********************************
738 * Functions called on keypresses. *
739 ***********************************/
740
741 static int
742 substitute_yes(ledit_view *view, char *key_text, size_t len, size_t lang…
743 (void)key_text; (void)len; (void)lang_index;
744 substitute_single(view);
745 buffer_recalc_line(view->buffer, sub_state.line);
746 if (!sub_state.global) {
747 sub_state.line++;
748 sub_state.byte = 0;
749 } else {
750 sub_state.byte += sub_state.rlen;
751 }
752 int ret = move_to_next_substitution(view);
753 if (!ret)
754 show_num_substituted(view);
755 return ret;
756 }
757
758 static int
759 substitute_yes_all(ledit_view *view, char *key_text, size_t len, size_t …
760 (void)key_text; (void)len; (void)lang_index;
761 substitute_all_remaining(view);
762 show_num_substituted(view);
763 return 0;
764 }
765
766 static int
767 substitute_no(ledit_view *view, char *key_text, size_t len, size_t lang_…
768 (void)key_text; (void)len; (void)lang_index;
769 if (!sub_state.global) {
770 sub_state.line++;
771 sub_state.byte = 0;
772 } else {
773 sub_state.byte += sub_state.slen;
774 }
775 int ret = move_to_next_substitution(view);
776 if (!ret)
777 show_num_substituted(view);
778 return ret;
779 }
780
781 static int
782 substitute_no_all(ledit_view *view, char *key_text, size_t len, size_t l…
783 (void)key_text; (void)len; (void)lang_index;
784 buffer_unlock_all_views(view->buffer);
785 show_num_substituted(view);
786 return 0;
787 }
788
789 static int
790 edit_insert_text(ledit_view *view, char *key_text, size_t len, size_t la…
791 (void)lang_index;
792 /* FIXME: overflow */
793 window_insert_bottom_bar_text(view->window, key_text, len);
794 window_set_bottom_bar_cursor(
795 view->window, ledit_window_get_bottom_bar_cursor(view->windo…
796 );
797 return 1;
798 }
799
800 static int
801 edit_cursor_to_end(ledit_view *view, char *key_text, size_t len, size_t …
802 (void)key_text; (void)len; (void)lang_index;
803 window_bottom_bar_cursor_to_end(view->window);
804 return 1;
805 }
806
807 static int
808 edit_cursor_to_beginning(ledit_view *view, char *key_text, size_t len, s…
809 (void)key_text; (void)len; (void)lang_index;
810 window_bottom_bar_cursor_to_beginning(view->window);
811 return 1;
812 }
813
814 static int
815 edit_cursor_left(ledit_view *view, char *key_text, size_t len, size_t la…
816 (void)key_text; (void)len; (void)lang_index;
817 window_move_bottom_bar_cursor(view->window, -1);
818 return 1;
819 }
820
821 static int
822 edit_cursor_right(ledit_view *view, char *key_text, size_t len, size_t l…
823 (void)key_text; (void)len; (void)lang_index;
824 window_move_bottom_bar_cursor(view->window, 1);
825 return 1;
826 }
827
828 static int
829 edit_backspace(ledit_view *view, char *key_text, size_t len, size_t lang…
830 (void)key_text; (void)len; (void)lang_index;
831 window_delete_bottom_bar_char(view->window, -1);
832 return 1;
833 }
834
835 static int
836 edit_delete(ledit_view *view, char *key_text, size_t len, size_t lang_in…
837 (void)key_text; (void)len; (void)lang_index;
838 window_delete_bottom_bar_char(view->window, 1);
839 return 1;
840 }
841
842 static int
843 edit_submit(ledit_view *view, char *key_text, size_t len, size_t lang_in…
844 (void)key_text; (void)len;
845 window_set_bottom_bar_text_shown(view->window, 0);
846 char *text = window_get_bottom_bar_text(view->window);
847 int min_pos = window_get_bottom_bar_min_pos(view->window);
848 int textlen = strlen(text);
849 /* this should never happen */
850 if (min_pos > textlen) {
851 textlen = 0;
852 } else {
853 textlen -= min_pos;
854 text += min_pos;
855 }
856 /* FIXME: this is hacky */
857 char *cmd = ledit_strndup(text, textlen);
858 int ret = handle_cmd(view, cmd, (size_t)textlen, lang_index);
859 free(cmd);
860 return ret;
861 }
862
863 static int
864 edit_prevcommand(ledit_view *view, char *key_text, size_t len, size_t la…
865 (void)key_text; (void)len; (void)lang_index;
866 if (cmdhistory.cur > 0) {
867 cmdhistory.cur--;
868 window_set_bottom_bar_realtext(view->window, cmdhistory.…
869 window_bottom_bar_cursor_to_end(view->window);
870 }
871 return 1;
872 }
873
874 static int
875 edit_nextcommand(ledit_view *view, char *key_text, size_t len, size_t la…
876 (void)key_text; (void)len; (void)lang_index;
877 if (cmdhistory.len > 0 && cmdhistory.cur < cmdhistory.len - 1) {
878 cmdhistory.cur++;
879 window_set_bottom_bar_realtext(view->window, cmdhistory.…
880 } else {
881 cmdhistory.cur = cmdhistory.len;
882 window_set_bottom_bar_realtext(view->window, "", -1);
883 }
884 window_bottom_bar_cursor_to_end(view->window);
885 return 1;
886 }
887
888 static int
889 edit_prevsearch(ledit_view *view, char *key_text, size_t len, size_t lan…
890 (void)key_text; (void)len; (void)lang_index;
891 if (searchhistory.cur > 0) {
892 searchhistory.cur--;
893 window_set_bottom_bar_realtext(view->window, searchhisto…
894 window_bottom_bar_cursor_to_end(view->window);
895 }
896 return 1;
897 }
898
899 static int
900 edit_nextsearch(ledit_view *view, char *key_text, size_t len, size_t lan…
901 (void)key_text; (void)len; (void)lang_index;
902 if (searchhistory.len > 0 && searchhistory.cur < searchhistory.l…
903 searchhistory.cur++;
904 window_set_bottom_bar_realtext(view->window, searchhisto…
905 } else {
906 searchhistory.cur = searchhistory.len;
907 window_set_bottom_bar_realtext(view->window, "", -1);
908 }
909 window_bottom_bar_cursor_to_end(view->window);
910 return 1;
911 }
912
913 /* FIXME: the current "highlight_search" support is a bit weird and will…
914 in some way if other support for visual mode (e.g. only search in sel…
915 /* FIXME: support visual mode, i.e. change selection to new place? */
916 /* FIXME: maybe have separate setting to allow highlighting search just …
917 mode (i.e. don't switch to visual mode automatically) */
918 void
919 search_next(ledit_view *view) {
920 view_wipe_line_cursor_attrs(view, view->cur_line);
921 size_t len = 0;
922 search_state ret = ledit_search_next(view, &view->cur_line, &vie…
923 ledit_theme *theme = config_get_theme();
924 /* FIXME: figure out key stack handling when modes are also chan…
925 if (theme->highlight_search && len > 0 && (ret == SEARCH_NORMAL …
926 view_set_mode(view, VISUAL);
927 view_set_selection(view, view->cur_line, view->cur_index…
928 } else if (view->mode == VISUAL) {
929 view_set_selection(view, view->cur_line, view->cur_index…
930 } else if (view->mode == NORMAL) {
931 view_set_line_cursor_attrs(view, view->cur_line, view->c…
932 }
933 view_ensure_cursor_shown(view);
934 if (ret != SEARCH_NORMAL)
935 window_show_message(view->window, search_state_to_str(re…
936 }
937
938 void
939 search_prev(ledit_view *view) {
940 view_wipe_line_cursor_attrs(view, view->cur_line);
941 size_t len = 0;
942 search_state ret = ledit_search_prev(view, &view->cur_line, &vie…
943 ledit_theme *theme = config_get_theme();
944 if (theme->highlight_search && len > 0 && (ret == SEARCH_NORMAL …
945 view_set_mode(view, VISUAL);
946 view_set_selection(view, view->cur_line, view->cur_index…
947 } else if (view->mode == VISUAL) {
948 view_set_selection(view, view->cur_line, view->cur_index…
949 } if (view->mode == NORMAL) {
950 view_set_line_cursor_attrs(view, view->cur_line, view->c…
951 }
952 view_ensure_cursor_shown(view);
953 if (ret != SEARCH_NORMAL)
954 window_show_message(view->window, search_state_to_str(re…
955 }
956
957 static int
958 editsearch_submit(ledit_view *view, char *key_text, size_t len, size_t l…
959 (void)key_text; (void)len; (void)lang_index;
960 window_set_bottom_bar_text_shown(view->window, 0);
961 char *text = window_get_bottom_bar_text(view->window);
962 int min_pos = window_get_bottom_bar_min_pos(view->window);
963 int textlen = strlen(text);
964 /* this should always be the case */
965 if (min_pos <= textlen) {
966 if (min_pos < textlen)
967 push_searchhistory(text + min_pos, textlen - min…
968 set_search_forward(text + min_pos);
969 search_next(view);
970 } else {
971 window_show_message(
972 view->window,
973 "Error in program. Tell lumidify about it.", -1
974 );
975 }
976 return 0;
977 }
978
979 static int
980 editsearchb_submit(ledit_view *view, char *key_text, size_t len, size_t …
981 (void)key_text; (void)len; (void)lang_index;
982 window_set_bottom_bar_text_shown(view->window, 0);
983 char *text = window_get_bottom_bar_text(view->window);
984 int min_pos = window_get_bottom_bar_min_pos(view->window);
985 int textlen = strlen(text);
986 /* this should always be the case */
987 if (min_pos <= textlen) {
988 if (min_pos < textlen)
989 push_searchhistory(text + min_pos, textlen - min…
990 set_search_backward(text + min_pos);
991 search_next(view);
992 } else {
993 window_show_message(
994 view->window,
995 "Error in program. Tell lumidify about it.", -1
996 );
997 }
998 return 0;
999 }
1000
1001 static int
1002 edit_discard(ledit_view *view, char *key_text, size_t len, size_t lang_i…
1003 (void)view; (void)key_text; (void)lang_index;
1004 (void)len;
1005 window_set_bottom_bar_text_shown(view->window, 0);
1006 return 0;
1007 }
1008
1009 struct action
1010 command_key_handler(ledit_view *view, unsigned int key_state, KeySym sym…
1011 command_key_array *cur_keys = config_get_command_keys(lang_index…
1012 size_t num_keys = cur_keys->num_keys;
1013 int grabkey = 1, found = 0;
1014 command_key_cb_flags flags = KEY_FLAG_NONE;
1015 for (size_t i = 0; i < num_keys; i++) {
1016 if (cur_keys->keys[i].text) {
1017 if (n > 0 &&
1018 (cur_keys->keys[i].modes & view->cur_command…
1019 ((!strncmp(cur_keys->keys[i].text, buf, n) &&
1020 match_key(cur_keys->keys[i].mods, key_stat…
1021 cur_keys->keys[i].text[0] == '\0')) {
1022 flags = cur_keys->keys[i].cb->flags;
1023 if (!(flags & KEY_FLAG_LOCK_ALLOWED) && …
1024 (void)view_locked_error(view);
1025 grabkey = 0;
1026 break;
1027 }
1028 grabkey = cur_keys->keys[i].cb->func(vie…
1029 found = 1;
1030 break;
1031 }
1032 } else if ((cur_keys->keys[i].modes & view->cur_command_…
1033 (cur_keys->keys[i].keysym == sym) &&
1034 (match_key(cur_keys->keys[i].mods, key_state)…
1035 flags = cur_keys->keys[i].cb->flags;
1036 if (!(flags & KEY_FLAG_LOCK_ALLOWED) && view->lo…
1037 (void)view_locked_error(view);
1038 grabkey = 0;
1039 break;
1040 }
1041 grabkey = cur_keys->keys[i].cb->func(view, buf, …
1042 found = 1;
1043 break;
1044 }
1045 }
1046 if (found && (flags & KEY_FLAG_JUMP_TO_CURSOR))
1047 view_ensure_cursor_shown(view);
1048 /* FIXME: proper error on invalid key */
1049 if (grabkey)
1050 return (struct action){ACTION_GRABKEY, &command_key_hand…
1051 else
1052 return (struct action){ACTION_NONE, NULL};
1053 }
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.