1 /* Yash: yet another shell */
2 /* editing.c: main editing module */
3 /* (C) 2007-2020 magicant */
4
5 /* This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18
19 #include "../common.h"
20 #include "editing.h"
21 #include <assert.h>
22 #include <errno.h>
23 #if HAVE_GETTEXT
24 # include <libintl.h>
25 #endif
26 #include <limits.h>
27 #include <stdbool.h>
28 #include <stddef.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <wchar.h>
36 #include <wctype.h>
37 #include "../alias.h"
38 #include "../exec.h"
39 #include "../expand.h"
40 #include "../history.h"
41 #include "../job.h"
42 #include "../option.h"
43 #include "../path.h"
44 #include "../plist.h"
45 #include "../redir.h"
46 #include "../strbuf.h"
47 #include "../util.h"
48 #include "../xfnmatch.h"
49 #include "../yash.h"
50 #include "complete.h"
51 #include "display.h"
52 #include "keymap.h"
53 #include "lineedit.h"
54 #include "terminfo.h"
55 #include "trie.h"
56
57
58 /* The type of pairs of a command and an argument. */
59 struct le_command_T {
60 le_command_func_T *func;
61 wchar_t arg;
62 };
63
64
65 /* The main buffer where the command line is edited.
66 * The contents of the buffer is divided into two parts: The first part is the
67 * main command line text that is input and edited by the user. The second is
68 * automatically appended after the first as a result of the prediction
69 * feature. As the user edits the first part, the prediction feature updates
70 * the second. When the user moves the cursor to somewhere in the second part,
71 * the text up to the cursor then becomes the first. */
72 xwcsbuf_T le_main_buffer;
73 /* The position that divides the main buffer into two parts as described just
74 * above. If `le_main_length > le_main_buffer.length', the second part is
75 * assumed empty. */
76 size_t le_main_length;
77 /* The position of the cursor on the command line.
78 * le_main_index <= le_main_buffer.length */
79 size_t le_main_index;
80
81 /* The history entry that is being edited in the main buffer now.
82 * When we're editing no history entry, `main_history_entry' is `Histlist'. */
83 static const histlink_T *main_history_entry;
84 /* The original value of `main_history_entry', converted into a wide string. */
85 static wchar_t *main_history_value;
86
87 /* The direction of currently performed command history search. */
88 enum le_search_direction_T le_search_direction;
89 /* The type of currently performed command history search. */
90 enum le_search_type_T le_search_type;
91 /* Supplementary buffer used in command history search.
92 * When search is not being performed, `le_search_buffer.contents' is NULL. */
93 xwcsbuf_T le_search_buffer;
94 /* The search result for the current value of `le_search_buffer'.
95 * If there is no match, `le_search_result' is `Histlist'. */
96 const histlink_T *le_search_result;
97 /* The search string and the direction of the last search. */
98 static struct {
99 enum le_search_direction_T direction;
100 enum le_search_type_T type;
101 wchar_t *value;
102 } last_search;
103
104 /* The last executed command and the currently executing command. */
105 static struct le_command_T last_command, current_command;
106
107 /* The type of motion expecting commands. */
108 enum motion_expect_command_T {
109 MEC_UPPERCASE = 1 << 0, /* convert the text to upper case */
110 MEC_LOWERCASE = 1 << 1, /* convert the text to lower case */
111 MEC_SWITCHCASE = MEC_UPPERCASE | MEC_LOWERCASE, /* switch case of text */
112 MEC_CASEMASK = MEC_SWITCHCASE,
113
114 MEC_TOSTART = 1 << 2, /* move cursor to the beginning of the region */
115 MEC_TOEND = 1 << 3, /* move cursor to the end of the region */
116 MEC_MOVE = MEC_TOSTART | MEC_TOEND, /* move cursor to motion end */
117 MEC_CURSORMASK = MEC_MOVE,
118 /* If none of MEC_TOSTART, MEC_TOEND, and MEC_MOVE is specified, the cursor
119 * is not moved unless MEC_DELETE is specified. */
120
121 MEC_COPY = 1 << 4, /* copy the text to the kill ring */
122 MEC_DELETE = 1 << 5, /* delete the text */
123 MEC_INSERT = 1 << 6, /* go to insert mode */
124 MEC_KILL = MEC_COPY | MEC_DELETE,
125 MEC_CHANGE = MEC_DELETE | MEC_INSERT,
126 MEC_COPYCHANGE = MEC_KILL | MEC_INSERT,
127 };
128
129 /* The state in which a command is executed. */
130 struct state_T {
131 struct {
132 /* When count is not specified, `sign' and `abs' are 0.
133 * Otherwise, `sign' is 1 or -1.
134 * When the negative sign is specified but digits are not, `abs' is 0.*/
135 int sign;
136 unsigned abs;
137 int multiplier;
138 } count;
139 enum motion_expect_command_T pending_command_motion;
140 le_command_func_T *pending_command_char;
141 };
142 #define COUNT_ABS_MAX 999999999
143
144 /* The current state. */
145 static struct state_T state;
146
147 /* The last executed editing command and the then state.
148 * Valid iff `.command.func' is non-null. */
149 static struct {
150 struct le_command_T command;
151 struct state_T state;
152 } last_edit_command;
153
154 /* The last executed find/till command. */
155 /* `last_find_command' is valid iff `.func' is non-null. */
156 static struct le_command_T last_find_command;
157
158 /* The editing mode before the mode is changed to LE_MODE_CHAR_EXPECT/SEARCH.
159 * When the char-expecting/search command finishes, the mode is restored to
160 * this mode. */
161 static le_mode_id_T savemode;
162
163 /* When starting the overwrite mode, the then `le_main_buffer' contents and
164 * `le_main_length' are saved in this structure. The values are kept so that the
165 * original contents can be restored when the user hits backspace. When the user
166 * leaves the overwrite mode, `contents' is freed and set to NULL. */
167 static struct {
168 wchar_t *contents;
169 size_t length;
170 } overwrite_save_buffer;
171
172 /* History of the edit line between editing commands. */
173 static plist_T undo_history;
174 /* Index of the current state in the history.
175 * If the current state is the newest, the index is `undo_history.length'. */
176 static size_t undo_index;
177 /* The history entry that is saved in the undo history. */
178 static const histlink_T *undo_history_entry;
179 /* The index that is to be the value of the `index' member of the next undo
180 * history entry. */
181 static size_t undo_save_index;
182 /* Structure of history entries */
183 struct undo_history {
184 size_t index; /* index of the cursor */
185 wchar_t contents[]; /* contents of the edit line */
186 // `contents' is a copy of `le_main_buffer.contents' up to `le_main_length'.
187 };
188
189 #define KILL_RING_SIZE 32 /* must be power of 2 */
190 /* The kill ring */
191 static wchar_t *kill_ring[KILL_RING_SIZE];
192 /* The index of the element to which next killed string is assigned. */
193 static size_t next_kill_index = 0;
194 /* The index of the last put element. */
195 static size_t last_put_elem = 0; /* < KILL_RING_SIZE */
196 /* The position and length of the last put string. */
197 static size_t last_put_range_start, last_put_range_length;
198
199 /* Set to true if the next completion command should restart completion from
200 * scratch. */
201 static bool reset_completion;
202 /* The next value of `reset_completion'. */
203 static bool next_reset_completion;
204
205 /* Probability distribution tree for command prediction. */
206 static trie_T *prediction_tree = NULL;
207
208
209 static void reset_state(void);
210 static void reset_count(void);
211 static int get_count(int default_value)
212 __attribute__((pure));
213 static size_t active_length(void)
214 __attribute__((pure));
215 static void save_current_edit_command(void);
216 static void save_current_find_command(void);
217 static void save_undo_history(void);
218 static void maybe_save_undo_history(void);
219 static void exec_motion_command(size_t new_index, bool inclusive);
220 static void set_motion_expect_command(enum motion_expect_command_T cmd);
221 static void exec_motion_expect_command(
222 enum motion_expect_command_T cmd, le_command_func_T motion);
223 static void exec_motion_expect_command_line(enum motion_expect_command_T cmd);
224 static void exec_motion_expect_command_all(void);
225 static void add_to_kill_ring(const wchar_t *s, size_t n)
226 __attribute__((nonnull));
227 static void set_char_expect_command(le_command_func_T cmd)
228 __attribute__((nonnull));
229 static void set_overwriting(bool overwriting);
230 static inline bool is_overwriting(void)
231 __attribute__((pure));
232 static void restore_overwritten_buffer_contents(
233 size_t start_index, size_t end_index);
234 static void set_search_mode(le_mode_id_T mode, enum le_search_direction_T dir);
235 static void to_upper_case(wchar_t *s, size_t n)
236 __attribute__((nonnull));
237 static void to_lower_case(wchar_t *s, size_t n)
238 __attribute__((nonnull));
239 static void switch_case(wchar_t *s, size_t n)
240 __attribute__((nonnull));
241
242 static void set_mode(le_mode_id_T newmode, bool overwrite);
243 static void redraw_all(bool clear);
244
245 static bool alert_if_first(void);
246 static bool alert_if_last(void);
247 static void move_cursor_forward_char(int offset);
248 static void move_cursor_backward_char(int offset);
249 static void move_cursor_forward_bigword(int count);
250 static void move_cursor_backward_bigword(int count);
251 static size_t next_bigword_index(const wchar_t *s, size_t i)
252 __attribute__((nonnull));
253 static size_t next_end_of_bigword_index(
254 const wchar_t *s, size_t i, bool progress)
255 __attribute__((nonnull));
256 static size_t previous_bigword_index(const wchar_t *s, size_t i)
257 __attribute__((nonnull));
258 static void move_cursor_forward_semiword(int count);
259 static void move_cursor_backward_semiword(int count);
260 static size_t next_semiword_index(const wchar_t *s, size_t i)
261 __attribute__((nonnull));
262 static size_t next_end_of_semiword_index(
263 const wchar_t *s, size_t i, bool progress)
264 __attribute__((nonnull));
265 static size_t previous_semiword_index(const wchar_t *s, size_t i)
266 __attribute__((nonnull));
267 static void move_cursor_forward_viword(int count);
268 static inline bool need_cw_treatment(void)
269 __attribute__((pure));
270 static void move_cursor_backward_viword(int count);
271 static size_t next_viword_index(const wchar_t *s, size_t i)
272 __attribute__((nonnull));
273 static size_t next_end_of_viword_index(
274 const wchar_t *s, size_t i, bool progress)
275 __attribute__((nonnull));
276 static size_t previous_viword_index(const wchar_t *s, size_t i)
277 __attribute__((nonnull));
278 static void move_cursor_forward_emacsword(int count);
279 static void move_cursor_backward_emacsword(int count);
280 static size_t next_emacsword_index(const wchar_t *s, size_t i)
281 __attribute__((nonnull));
282 static size_t previous_emacsword_index(const wchar_t *s, size_t i)
283 __attribute__((nonnull));
284 static void find_char(wchar_t c);
285 static void find_char_rev(wchar_t c);
286 static void till_char(wchar_t c);
287 static void till_char_rev(wchar_t c);
288 static void exec_find(wchar_t c, int count, bool till);
289 static size_t find_nth_occurence(wchar_t c, int n);
290
291 static void put_killed_string(bool after_cursor, bool cursor_on_last_char);
292 static void insert_killed_string(
293 bool after_cursor, bool cursor_on_last_char, size_t index);
294 static void cancel_undo(int offset);
295
296 static void check_reset_completion(void);
297
298 static void create_prediction_tree(void);
299 static size_t count_matching_previous_commands(const histentry_T *e1)
300 __attribute__((nonnull,pure));
301 static void clear_prediction(void);
302 static void update_buffer_with_prediction(void);
303
304 static void vi_replace_char(wchar_t c);
305 static void vi_exec_alias(wchar_t c);
306 struct xwcsrange { const wchar_t *start, *end; };
307 static struct xwcsrange get_next_bigword(const wchar_t *s)
308 __attribute__((nonnull));
309 static struct xwcsrange get_prev_bigword(
310 const wchar_t *beginning, const wchar_t *s)
311 __attribute__((nonnull));
312
313 static void replace_horizontal_space(bool deleteafter, const wchar_t *s)
314 __attribute__((nonnull));
315
316 static void go_to_history_absolute(
317 const histlink_T *l, enum le_search_type_T curpos)
318 __attribute__((nonnull));
319 static void go_to_history_relative(int offset, enum le_search_type_T curpos);
320 static void go_to_history(const histlink_T *l, enum le_search_type_T curpos)
321 __attribute__((nonnull));
322
323 static bool need_update_last_search_value(void)
324 __attribute__((pure));
325 static void update_search(void);
326 static void perform_search(const wchar_t *pattern,
327 enum le_search_direction_T dir, enum le_search_type_T type)
328 __attribute__((nonnull));
329 static void search_again(enum le_search_direction_T dir);
330 static void beginning_search(enum le_search_direction_T dir);
331 static inline bool beginning_search_check_go_to_history(const wchar_t *prefix)
332 __attribute__((nonnull,pure));
333
334 #define ALERT_AND_RETURN_IF_PENDING \
335 do if (state.pending_command_motion != MEC_MOVE) \
336 { cmd_alert(L'\0'); return; } \
337 while (0)
338
339
340 /* Initializes the editing module before starting editing. */
le_editing_init(void)341 void le_editing_init(void)
342 {
343 wb_init(&le_main_buffer);
344 le_main_length = le_main_index = 0;
345 main_history_entry = Histlist;
346 main_history_value = xwcsdup(L"");
347
348 switch (shopt_lineedit) {
349 case SHOPT_VI: le_set_mode(LE_MODE_VI_INSERT); break;
350 case SHOPT_EMACS: le_set_mode(LE_MODE_EMACS); break;
351 default: assert(false);
352 }
353
354 last_command.func = 0;
355 last_command.arg = L'\0';
356
357 start_using_history();
358 pl_init(&undo_history);
359 undo_index = 0;
360 undo_save_index = le_main_index;
361 undo_history_entry = Histlist;
362 save_undo_history();
363
364 reset_completion = true;
365
366 reset_state();
367 set_overwriting(false);
368
369 if (shopt_le_predict) {
370 create_prediction_tree();
371 update_buffer_with_prediction();
372 }
373 }
374
375 /* Finalizes the editing module when editing is finished.
376 * Returns the content of the main buffer, which must be freed by the caller. */
le_editing_finalize(void)377 wchar_t *le_editing_finalize(void)
378 {
379 assert(le_search_buffer.contents == NULL);
380
381 plfree(pl_toary(&undo_history), free);
382
383 le_complete_cleanup();
384
385 end_using_history();
386 free(main_history_value);
387
388 clear_prediction();
389 trie_destroy(prediction_tree), prediction_tree = NULL;
390 wb_wccat(&le_main_buffer, L'\n');
391 return wb_towcs(&le_main_buffer);
392 }
393
394 /* Invokes the specified command. */
le_invoke_command(le_command_func_T * cmd,wchar_t arg)395 void le_invoke_command(le_command_func_T *cmd, wchar_t arg)
396 {
397 current_command.func = cmd;
398 current_command.arg = arg;
399
400 next_reset_completion = true;
401
402 cmd(arg);
403
404 last_command = current_command;
405
406 reset_completion |= next_reset_completion;
407
408 if (le_main_length < le_main_index)
409 le_main_length = le_main_index;
410 switch (le_editstate) {
411 case LE_EDITSTATE_EDITING:
412 if (shopt_le_predict)
413 update_buffer_with_prediction();
414 break;
415 case LE_EDITSTATE_DONE:
416 case LE_EDITSTATE_ERROR:
417 clear_prediction();
418 break;
419 case LE_EDITSTATE_INTERRUPTED:
420 break;
421 }
422
423 if (LE_CURRENT_MODE == LE_MODE_VI_COMMAND)
424 if (le_main_index > 0 && le_main_index == le_main_buffer.length)
425 le_main_index--;
426 }
427
428 /* Resets `state'. */
reset_state(void)429 void reset_state(void)
430 {
431 reset_count();
432 state.pending_command_motion = MEC_MOVE;
433 state.pending_command_char = 0;
434 }
435
436 /* Resets `state.count'. */
reset_count(void)437 void reset_count(void)
438 {
439 state.count.sign = 0;
440 state.count.abs = 0;
441 state.count.multiplier = 1;
442 }
443
444 /* Returns the count value.
445 * If the count is not set, returns the `default_value'. */
get_count(int default_value)446 int get_count(int default_value)
447 {
448 long long result;
449
450 if (state.count.sign == 0)
451 result = (long long) default_value * state.count.multiplier;
452 else if (state.count.sign < 0 && state.count.abs == 0)
453 result = (long long) -state.count.multiplier;
454 else
455 result = (long long) state.count.abs * state.count.sign *
456 state.count.multiplier;
457
458 if (result < -COUNT_ABS_MAX)
459 result = -COUNT_ABS_MAX;
460 else if (result > COUNT_ABS_MAX)
461 result = COUNT_ABS_MAX;
462 return result;
463 }
464
465 /* Returns the length of the first part of `le_main_buffer'. */
active_length(void)466 size_t active_length(void)
467 {
468 if (le_main_length > le_main_buffer.length)
469 return le_main_buffer.length;
470 if (le_main_length < le_main_index)
471 return le_main_index;
472 return le_main_length;
473 }
474
475 /* Saves the currently executing command and the current state in
476 * `last_edit_command' if we are not redoing and the mode is not "vi insert". */
save_current_edit_command(void)477 void save_current_edit_command(void)
478 {
479 if (current_command.func != cmd_redo
480 && LE_CURRENT_MODE != LE_MODE_VI_INSERT) {
481 last_edit_command.command = current_command;
482 last_edit_command.state = state;
483 }
484 }
485
486 /* Saves the currently executing command and the current state in
487 * `last_find_command' if we are not redoing/refinding. */
save_current_find_command(void)488 void save_current_find_command(void)
489 {
490 if (current_command.func != cmd_refind_char
491 && current_command.func != cmd_refind_char_rev
492 && current_command.func != cmd_redo)
493 last_find_command = current_command;
494 }
495
496 /* Saves the current contents of the edit line to the undo history.
497 * History entries at the current `undo_index' and newer are removed before
498 * saving the current. If `undo_history_entry' is different from
499 * `main_history_entry', all undo history entries are removed. */
save_undo_history(void)500 void save_undo_history(void)
501 {
502 for (size_t i = undo_index; i < undo_history.length; i++)
503 free(undo_history.contents[i]);
504 pl_truncate(&undo_history, undo_index);
505
506 // No need to check for overflow in `len + 1' here. Should overflow occur,
507 // the buffer would not have been allocated successfully.
508 size_t len = active_length();
509 struct undo_history *e = xmallocs(sizeof *e, len + 1, sizeof *e->contents);
510 e->index = le_main_index;
511 wcsncpy(e->contents, le_main_buffer.contents, len);
512 e->contents[len] = L'\0';
513 pl_add(&undo_history, e);
514 assert(undo_index == undo_history.length - 1);
515 undo_history_entry = main_history_entry;
516 }
517
518 /* Calls `save_undo_history' if the current contents of the edit line is not
519 * saved. */
maybe_save_undo_history(void)520 void maybe_save_undo_history(void)
521 {
522 assert(undo_index <= undo_history.length);
523
524 size_t save_undo_save_index = undo_save_index;
525 undo_save_index = le_main_index;
526
527 size_t len = active_length();
528 if (undo_history_entry == main_history_entry) {
529 if (undo_index < undo_history.length) {
530 struct undo_history *h = undo_history.contents[undo_index];
531 if (wcsncmp(le_main_buffer.contents, h->contents, len) == 0 &&
532 h->contents[len] == L'\0') {
533 /* The contents of the main buffer is the same as saved in the
534 * history. Just save the index. */
535 h->index = le_main_index;
536 return;
537 }
538 undo_index++;
539 }
540 } else {
541 if (wcsncmp(le_main_buffer.contents, main_history_value, len) == 0 &&
542 main_history_value[len] == L'\0')
543 return;
544
545 /* The contents of the buffer has been changed from the value of the
546 * history entry, but it's not yet saved in the undo history. We first
547 * save the original history value and then save the current buffer
548 * contents. */
549 struct undo_history *h;
550 pl_clear(&undo_history, free);
551 h = xmallocs(sizeof *h,
552 add(wcslen(main_history_value), 1), sizeof *h->contents);
553 assert(save_undo_save_index <= wcslen(main_history_value));
554 h->index = save_undo_save_index;
555 wcscpy(h->contents, main_history_value);
556 pl_add(&undo_history, h);
557 undo_index = 1;
558 }
559 save_undo_history();
560 }
561
562 /* Applies the currently pending editing command to the range between the
563 * current cursor index and the specified index. If no editing command is
564 * pending, simply moves the cursor to the specified index. */
565 /* This function is used for all cursor-moving commands, even when not in the
566 * vi mode. */
exec_motion_command(size_t new_index,bool inclusive)567 void exec_motion_command(size_t new_index, bool inclusive)
568 {
569 assert(le_main_index <= le_main_buffer.length);
570 assert(new_index <= le_main_buffer.length);
571
572 size_t old_index = le_main_index;
573 size_t start_index, end_index;
574 if (old_index <= new_index)
575 start_index = old_index, end_index = new_index;
576 else
577 start_index = new_index, end_index = old_index;
578 if (inclusive && end_index < le_main_buffer.length)
579 end_index++;
580
581 enum motion_expect_command_T mec = state.pending_command_motion;
582
583 /* don't save undo history when repeating backspace */
584 bool repeated_backspace = (mec & MEC_DELETE)
585 && (new_index + 1 == old_index)
586 && current_command.func == cmd_backward_delete_char
587 && last_command.func == cmd_backward_delete_char;
588 if (!repeated_backspace)
589 maybe_save_undo_history();
590
591 if (mec & MEC_COPY) {
592 add_to_kill_ring(&le_main_buffer.contents[start_index],
593 end_index - start_index);
594 }
595 if (mec & MEC_CASEMASK) {
596 void (*case_func)(wchar_t *, size_t);
597 INIT(case_func, 0);
598 switch (mec & MEC_CASEMASK) {
599 case MEC_UPPERCASE:
600 case_func = to_upper_case;
601 break;
602 case MEC_LOWERCASE:
603 case_func = to_lower_case;
604 break;
605 case MEC_SWITCHCASE:
606 case_func = switch_case;
607 break;
608 }
609 case_func(&le_main_buffer.contents[start_index],
610 end_index - start_index);
611 if (le_main_length < end_index)
612 le_main_length = end_index;
613 }
614 switch (mec & MEC_CURSORMASK) {
615 case MEC_TOSTART: le_main_index = start_index; break;
616 case MEC_TOEND: le_main_index = end_index; break;
617 case MEC_MOVE: le_main_index = new_index; break;
618 }
619 if (mec & MEC_DELETE) {
620 save_current_edit_command();
621 clear_prediction();
622 if (!is_overwriting() || old_index <= new_index)
623 wb_remove(&le_main_buffer, start_index, end_index - start_index);
624 else
625 restore_overwritten_buffer_contents(start_index, end_index);
626 le_main_index = start_index;
627 }
628 if (mec & MEC_INSERT) {
629 le_set_mode(LE_MODE_VI_INSERT);
630 set_overwriting(false);
631 }
632 reset_state();
633 }
634
635 /* Sets the specified motion expecting command as pending.
636 * If the command is already pending, the command is executed on the whole
637 * line. */
set_motion_expect_command(enum motion_expect_command_T cmd)638 void set_motion_expect_command(enum motion_expect_command_T cmd)
639 {
640 if (state.pending_command_motion == MEC_MOVE) {
641 state.count.multiplier = get_count(1);
642 state.count.sign = 0;
643 state.count.abs = 0;
644 state.pending_command_motion = cmd;
645 } else {
646 if (state.pending_command_motion == cmd)
647 exec_motion_expect_command_all();
648 else
649 cmd_alert(L'\0');
650 }
651 }
652
653 /* Executes the specified motion expecting command with the specified motion
654 * command. */
exec_motion_expect_command(enum motion_expect_command_T cmd,le_command_func_T motion)655 void exec_motion_expect_command(
656 enum motion_expect_command_T cmd, le_command_func_T motion)
657 {
658 if (current_command.func != cmd_redo)
659 ALERT_AND_RETURN_IF_PENDING;
660 state.pending_command_motion = cmd;
661 motion(L'\0');
662 }
663
664 /* Executes the specified motion expecting command from the beginning to the end
665 * of the line. */
exec_motion_expect_command_line(enum motion_expect_command_T cmd)666 void exec_motion_expect_command_line(enum motion_expect_command_T cmd)
667 {
668 if (current_command.func != cmd_redo)
669 ALERT_AND_RETURN_IF_PENDING;
670 state.pending_command_motion = cmd;
671 exec_motion_expect_command_all();
672 }
673
674 /* Executes the currently pending motion expecting command from the beginning to
675 * the end of the line. */
exec_motion_expect_command_all(void)676 void exec_motion_expect_command_all(void)
677 {
678 size_t save_index = le_main_index;
679 enum motion_expect_command_T save_pending = state.pending_command_motion;
680
681 le_main_index = 0;
682 cmd_end_of_line(L'\0');
683 if (!(save_pending & (MEC_DELETE | MEC_CURSORMASK)))
684 le_main_index = save_index;
685 }
686
687 /* Adds the specified string to the kill ring.
688 * The maximum number of characters that are added is specified by `n'. */
add_to_kill_ring(const wchar_t * s,size_t n)689 void add_to_kill_ring(const wchar_t *s, size_t n)
690 {
691 if (n > 0 && s[0] != L'\0') {
692 free(kill_ring[next_kill_index]);
693 kill_ring[next_kill_index] = xwcsndup(s, n);
694 next_kill_index = (next_kill_index + 1) % KILL_RING_SIZE;
695 }
696 }
697
698 /* Sets the editing mode to "char expect" and the pending command to `cmd'.
699 * The current editing mode is saved in `savemode'. */
set_char_expect_command(le_command_func_T cmd)700 void set_char_expect_command(le_command_func_T cmd)
701 {
702 savemode = LE_CURRENT_MODE;
703 le_set_mode(LE_MODE_CHAR_EXPECT);
704 state.pending_command_char = cmd;
705 }
706
707 /* Enables or disables the overwrite mode. */
set_overwriting(bool overwrite)708 void set_overwriting(bool overwrite)
709 {
710 free(overwrite_save_buffer.contents);
711 if (overwrite) {
712 size_t len = active_length();
713 overwrite_save_buffer.contents = xwcsndup(le_main_buffer.contents, len);
714 overwrite_save_buffer.length = len;
715 } else {
716 overwrite_save_buffer.contents = NULL;
717 }
718 }
719
720 /* Returns true iff the overwrite mode is active. */
is_overwriting(void)721 bool is_overwriting(void)
722 {
723 return overwrite_save_buffer.contents != NULL;
724 }
725
726 /* Restores the main buffer contents that were overwritten in the current
727 * overwrite mode. When called, `le_main_length >= le_main_buffer.length' must
728 * hold. The caller must adjust `le_main_index' because this function may remove
729 * some characters from `le_main_buffer'. */
restore_overwritten_buffer_contents(size_t start_index,size_t end_index)730 void restore_overwritten_buffer_contents(size_t start_index, size_t end_index)
731 {
732 size_t mid_index;
733 if (overwrite_save_buffer.length < start_index)
734 mid_index = start_index;
735 else if (overwrite_save_buffer.length > end_index)
736 mid_index = end_index;
737 else
738 mid_index = overwrite_save_buffer.length;
739
740 /* Restore contents from `start_index' to `mid_index' */
741 wmemcpy(&le_main_buffer.contents[start_index],
742 &overwrite_save_buffer.contents[start_index],
743 mid_index - start_index);
744
745 /* Contents from `mid_index' to `end_index' were actually not overwritten
746 * but appended, so they should be removed. */
747 wb_remove(&le_main_buffer, mid_index, end_index - mid_index);
748 }
749
750 /* Starts command history search by setting the editing mode to `mode' with
751 * the specified direction `dir'. `mode' must be either LE_MODE_VI_SEARCH or
752 * LE_MODE_EMACS_SEARCH.
753 * The current editing mode is saved in `savemode'. */
set_search_mode(le_mode_id_T mode,enum le_search_direction_T dir)754 void set_search_mode(le_mode_id_T mode, enum le_search_direction_T dir)
755 {
756 le_complete_cleanup();
757
758 savemode = LE_CURRENT_MODE;
759 le_set_mode(mode);
760 le_search_direction = dir;
761 switch (mode) {
762 case LE_MODE_VI_SEARCH: le_search_type = SEARCH_VI; break;
763 case LE_MODE_EMACS_SEARCH: le_search_type = SEARCH_EMACS; break;
764 default: assert(false);
765 }
766 wb_init(&le_search_buffer);
767 update_search();
768 }
769
770 /* Converts the first `n' characters of string `s' to upper case.
771 * The string must be at least `n' characters long. */
to_upper_case(wchar_t * s,size_t n)772 void to_upper_case(wchar_t *s, size_t n)
773 {
774 for (size_t i = 0; i < n; i++)
775 s[i] = towupper(s[i]);
776 }
777
778 /* Converts the first `n' characters of string `s' to lower case.
779 * The string must be at least `n' characters long. */
to_lower_case(wchar_t * s,size_t n)780 void to_lower_case(wchar_t *s, size_t n)
781 {
782 for (size_t i = 0; i < n; i++)
783 s[i] = towlower(s[i]);
784 }
785
786 /* Switches case of the first `n' characters of string `s'.
787 * The string must be at least `n' characters long. */
switch_case(wchar_t * s,size_t n)788 void switch_case(wchar_t *s, size_t n)
789 {
790 for (size_t i = 0; i < n; i++) {
791 wchar_t c = s[i];
792 s[i] = iswlower(c) ? towupper(c) : towlower(c);
793 }
794 }
795
796
797 /********** Basic Commands **********/
798
799 /* Does nothing. */
cmd_noop(wchar_t c)800 void cmd_noop(wchar_t c __attribute__((unused)))
801 {
802 next_reset_completion = false;
803 reset_state();
804 }
805
806 /* Alerts. */
cmd_alert(wchar_t c)807 void cmd_alert(wchar_t c __attribute__((unused)))
808 {
809 lebuf_print_alert(true);
810 reset_state();
811 }
812
813 /* Inserts the character argument into the buffer.
814 * If the count is set, inserts `count' times.
815 * If `is_overwriting()' is true, overwrites the character instead of inserting.
816 */
cmd_self_insert(wchar_t c)817 void cmd_self_insert(wchar_t c)
818 {
819 ALERT_AND_RETURN_IF_PENDING;
820 if (c == L'\0') {
821 cmd_alert(L'\0');
822 return;
823 }
824 clear_prediction();
825
826 int count = get_count(1);
827 while (--count >= 0)
828 if (is_overwriting() && le_main_index < le_main_buffer.length)
829 le_main_buffer.contents[le_main_index++] = c;
830 else
831 wb_ninsert_force(&le_main_buffer, le_main_index++, &c, 1);
832 reset_state();
833 }
834
835 /* Inserts the tab character. */
cmd_insert_tab(wchar_t c)836 void cmd_insert_tab(wchar_t c __attribute__((unused)))
837 {
838 cmd_self_insert(L'\t');
839 }
840
841 /* Sets the `le_next_verbatim' flag.
842 * The next character will be input to the main buffer even if it's a special
843 * character. */
cmd_expect_verbatim(wchar_t c)844 void cmd_expect_verbatim(wchar_t c __attribute__((unused)))
845 {
846 le_next_verbatim = true;
847 }
848
849 /* Adds the specified digit to the accumulating argument. */
850 /* If `c' is not a digit or a hyphen, does nothing. */
cmd_digit_argument(wchar_t c)851 void cmd_digit_argument(wchar_t c)
852 {
853 if (L'0' <= c && c <= L'9') {
854 if (state.count.abs > COUNT_ABS_MAX / 10) {
855 cmd_alert(L'\0'); // argument too large
856 return;
857 }
858 if (state.count.sign == 0)
859 state.count.sign = 1;
860 state.count.abs = state.count.abs * 10 + (unsigned) (c - L'0');
861 } else if (c == L'-') {
862 if (state.count.sign == 0)
863 state.count.sign = -1;
864 else
865 state.count.sign = -state.count.sign;
866 }
867
868 next_reset_completion = false;
869 }
870
871 /* If the count is not set, moves the cursor to the beginning of the line.
872 * Otherwise, adds the given digit to the count. */
cmd_bol_or_digit(wchar_t c)873 void cmd_bol_or_digit(wchar_t c)
874 {
875 if (state.count.sign == 0)
876 cmd_beginning_of_line(c);
877 else
878 cmd_digit_argument(c);
879 }
880
881 /* Accepts the current line.
882 * `le_editstate' is set to LE_EDITSTATE_DONE to induce line-editing to
883 * terminate.
884 * If history search is currently active, the search result is accepted. If the
885 * search was failing, the line is not accepted. */
cmd_accept_line(wchar_t c)886 void cmd_accept_line(wchar_t c __attribute__((unused)))
887 {
888 ALERT_AND_RETURN_IF_PENDING;
889
890 if (le_search_buffer.contents == NULL) {
891 le_editstate = LE_EDITSTATE_DONE;
892 reset_state();
893 } else {
894 if (le_search_result != Histlist)
895 le_editstate = LE_EDITSTATE_DONE;
896 cmd_srch_accept_search(L'\0');
897 }
898 }
899
900 /* Aborts the current line.
901 * `le_editstate' is set to LE_EDITSTATE_INTERRUPTED to induce line-editing to
902 * terminate. */
cmd_abort_line(wchar_t c)903 void cmd_abort_line(wchar_t c __attribute__((unused)))
904 {
905 cmd_srch_abort_search(L'\0');
906 le_editstate = LE_EDITSTATE_INTERRUPTED;
907 reset_state();
908 }
909
910 /* Sets `le_editstate' to LE_EDITSTATE_ERROR.
911 * The `le_readline' function will return INPUT_EOF. */
cmd_eof(wchar_t c)912 void cmd_eof(wchar_t c __attribute__((unused)))
913 {
914 ALERT_AND_RETURN_IF_PENDING;
915
916 cmd_srch_abort_search(L'\0');
917 le_editstate = LE_EDITSTATE_ERROR;
918 reset_state();
919 }
920
921 /* If the edit line is empty, sets `le_editstate' to LE_EDITSTATE_ERROR (return
922 * EOF). Otherwise, alerts. */
cmd_eof_if_empty(wchar_t c)923 void cmd_eof_if_empty(wchar_t c __attribute__((unused)))
924 {
925 if (active_length() == 0)
926 cmd_eof(L'\0');
927 else
928 cmd_alert(L'\0');
929 }
930
931 /* If the edit line is empty, sets `le_editstate' to LE_EDITSTATE_ERROR (return
932 * EOF). Otherwise, deletes the character under the cursor. */
cmd_eof_or_delete(wchar_t c)933 void cmd_eof_or_delete(wchar_t c __attribute__((unused)))
934 {
935 if (active_length() == 0)
936 cmd_eof(L'\0');
937 else
938 cmd_delete_char(L'\0');
939 }
940
941 /* Inserts a hash sign ('#') at the beginning of the line and accepts the line.
942 * If any count is set and the line already begins with a hash sign, the hash
943 * sign is removed rather than added. The line is accepted anyway. */
cmd_accept_with_hash(wchar_t c)944 void cmd_accept_with_hash(wchar_t c __attribute__((unused)))
945 {
946 ALERT_AND_RETURN_IF_PENDING;
947 clear_prediction();
948
949 if (state.count.sign == 0 || le_main_buffer.contents[0] != L'#')
950 wb_insert(&le_main_buffer, 0, L"#");
951 else
952 wb_remove(&le_main_buffer, 0, 1);
953 le_main_index = 0;
954 cmd_accept_line(L'\0');
955 }
956
957 /* Accept the current line including the prediction. */
cmd_accept_prediction(wchar_t c)958 void cmd_accept_prediction(wchar_t c)
959 {
960 cmd_accept_line(c);
961 if (le_editstate == LE_EDITSTATE_DONE)
962 le_main_length = SIZE_MAX;
963 }
964
965 /* Changes the editing mode to "vi insert". */
cmd_setmode_viinsert(wchar_t c)966 void cmd_setmode_viinsert(wchar_t c __attribute__((unused)))
967 {
968 set_mode(LE_MODE_VI_INSERT, false);
969 }
970
971 /* Changes the editing mode to "vi command". */
cmd_setmode_vicommand(wchar_t c)972 void cmd_setmode_vicommand(wchar_t c __attribute__((unused)))
973 {
974 set_mode(LE_MODE_VI_COMMAND, false);
975 }
976
977 /* Changes the editing mode to "emacs". */
cmd_setmode_emacs(wchar_t c)978 void cmd_setmode_emacs(wchar_t c __attribute__((unused)))
979 {
980 set_mode(LE_MODE_EMACS, false);
981 }
982
983 /* Changes the editing mode to the specified one. */
set_mode(le_mode_id_T newmode,bool overwrite)984 void set_mode(le_mode_id_T newmode, bool overwrite)
985 {
986 ALERT_AND_RETURN_IF_PENDING;
987 maybe_save_undo_history();
988
989 if (LE_CURRENT_MODE == LE_MODE_VI_INSERT && newmode == LE_MODE_VI_COMMAND)
990 if (le_main_index > 0)
991 le_main_index--;
992
993 le_set_mode(newmode);
994 set_overwriting(overwrite);
995 reset_state();
996 }
997
998 /* Executes the currently pending char-expecting command. */
cmd_expect_char(wchar_t c)999 void cmd_expect_char(wchar_t c)
1000 {
1001 if (!state.pending_command_char) {
1002 cmd_alert(L'\0');
1003 return;
1004 }
1005
1006 current_command.func = state.pending_command_char;
1007 current_command.arg = c;
1008 state.pending_command_char(c);
1009 }
1010
1011 /* Cancels the currently pending char-expecting command. */
cmd_abort_expect_char(wchar_t c)1012 void cmd_abort_expect_char(wchar_t c __attribute__((unused)))
1013 {
1014 if (!state.pending_command_char) {
1015 cmd_alert(L'\0');
1016 return;
1017 }
1018
1019 le_set_mode(savemode);
1020 reset_state();
1021 }
1022
1023 /* Redraws everything. */
cmd_redraw_all(wchar_t c)1024 void cmd_redraw_all(wchar_t c __attribute__((unused)))
1025 {
1026 redraw_all(false);
1027 }
1028
1029 /* Clears the screen and redraws everything at the top of the screen. */
cmd_clear_and_redraw_all(wchar_t c)1030 void cmd_clear_and_redraw_all(wchar_t c __attribute__((unused)))
1031 {
1032 redraw_all(true);
1033 }
1034
redraw_all(bool clear)1035 void redraw_all(bool clear)
1036 {
1037 next_reset_completion = false;
1038
1039 le_display_clear(clear);
1040 le_restore_terminal();
1041 le_setupterm(false);
1042 le_set_terminal();
1043 }
1044
1045
1046 /********** Motion Commands **********/
1047
1048 /* Invokes `cmd_alert' and returns true if the cursor is on the first character.
1049 */
alert_if_first(void)1050 bool alert_if_first(void)
1051 {
1052 if (le_main_index > 0)
1053 return false;
1054
1055 cmd_alert(L'\0');
1056 return true;
1057 }
1058
1059 /* Invokes `cmd_alert' and returns true if the cursor is on the last character.
1060 */
alert_if_last(void)1061 bool alert_if_last(void)
1062 {
1063 if (LE_CURRENT_MODE == LE_MODE_VI_COMMAND) {
1064 if (state.pending_command_motion != MEC_MOVE)
1065 return false;
1066 if (le_main_buffer.length > 0
1067 && le_main_index < le_main_buffer.length - 1)
1068 return false;
1069 } else {
1070 if (le_main_index < le_main_buffer.length)
1071 return false;
1072 }
1073
1074 cmd_alert(L'\0');
1075 return true;
1076 }
1077
1078 /* Moves forward one character (or `count' characters if the count is set). */
1079 /* exclusive motion command */
cmd_forward_char(wchar_t c)1080 void cmd_forward_char(wchar_t c __attribute__((unused)))
1081 {
1082 int count = get_count(1);
1083 if (count >= 0)
1084 move_cursor_forward_char(count);
1085 else
1086 move_cursor_backward_char(-count);
1087 }
1088
1089 /* Moves backward one character (or `count' characters if the count is set). */
1090 /* exclusive motion command */
cmd_backward_char(wchar_t c)1091 void cmd_backward_char(wchar_t c __attribute__((unused)))
1092 {
1093 int count = get_count(1);
1094 if (count >= 0)
1095 move_cursor_backward_char(count);
1096 else
1097 move_cursor_forward_char(-count);
1098 }
1099
1100 /* Moves the cursor forward by `offset'. The `offset' must not be negative. */
move_cursor_forward_char(int offset)1101 void move_cursor_forward_char(int offset)
1102 {
1103 assert(offset >= 0);
1104 if (alert_if_last())
1105 return;
1106
1107 #if COUNT_ABS_MAX > SIZE_MAX
1108 if (offset > SIZE_MAX)
1109 offset = SIZE_MAX;
1110 #endif
1111
1112 size_t new_index;
1113 if (le_main_buffer.length - le_main_index < (size_t) offset)
1114 new_index = le_main_buffer.length;
1115 else
1116 new_index = le_main_index + offset;
1117 exec_motion_command(new_index, false);
1118 }
1119
1120 /* Moves the cursor backward by `offset'. The `offset' must not be negative. */
move_cursor_backward_char(int offset)1121 void move_cursor_backward_char(int offset)
1122 {
1123 assert(offset >= 0);
1124 if (alert_if_first())
1125 return;
1126
1127 size_t new_index;
1128 #if COUNT_ABS_MAX > SIZE_MAX
1129 if ((int) le_main_index <= offset)
1130 #else
1131 if (le_main_index <= (size_t) offset)
1132 #endif
1133 new_index = 0;
1134 else
1135 new_index = le_main_index - offset;
1136 exec_motion_command(new_index, false);
1137 }
1138
1139 /* Moves forward one bigword (or `count' bigwords if the count is set). */
1140 /* exclusive motion command */
cmd_forward_bigword(wchar_t c)1141 void cmd_forward_bigword(wchar_t c __attribute__((unused)))
1142 {
1143 int count = get_count(1);
1144 if (count >= 0)
1145 move_cursor_forward_bigword(count);
1146 else
1147 move_cursor_backward_bigword(-count);
1148 }
1149
1150 /* Moves the cursor to the end of the current bigword (or the next bigword if
1151 * already at the end). If the count is set, moves to the end of `count'th
1152 * bigword. */
1153 /* inclusive motion command */
cmd_end_of_bigword(wchar_t c)1154 void cmd_end_of_bigword(wchar_t c __attribute__((unused)))
1155 {
1156 if (alert_if_last())
1157 return;
1158
1159 int count = get_count(1);
1160 size_t new_index = le_main_index;
1161 while (--count >= 0 && new_index < le_main_buffer.length)
1162 new_index = next_end_of_bigword_index(
1163 le_main_buffer.contents, new_index, true);
1164 exec_motion_command(new_index, true);
1165 }
1166
1167 /* Moves backward one bigword (or `count' bigwords if the count is set). */
1168 /* exclusive motion command */
cmd_backward_bigword(wchar_t c)1169 void cmd_backward_bigword(wchar_t c __attribute__((unused)))
1170 {
1171 int count = get_count(1);
1172 if (count >= 0)
1173 move_cursor_backward_bigword(count);
1174 else
1175 move_cursor_forward_bigword(-count);
1176 }
1177
1178 /* Moves the cursor forward `count' bigwords.
1179 * If `count' is negative, the cursor is not moved. */
move_cursor_forward_bigword(int count)1180 void move_cursor_forward_bigword(int count)
1181 {
1182 if (alert_if_last())
1183 return;
1184
1185 size_t new_index = le_main_index;
1186 if (!need_cw_treatment()) {
1187 while (count-- > 0 && new_index < le_main_buffer.length)
1188 new_index = next_bigword_index(le_main_buffer.contents, new_index);
1189 exec_motion_command(new_index, false);
1190 } else {
1191 while (count > 1 && new_index < le_main_buffer.length) {
1192 new_index = next_bigword_index(le_main_buffer.contents, new_index);
1193 count--;
1194 }
1195 if (count > 0 && new_index < le_main_buffer.length) {
1196 new_index = next_end_of_bigword_index(
1197 le_main_buffer.contents, new_index, false);
1198 }
1199 exec_motion_command(new_index, true);
1200 }
1201 }
1202
1203 /* Moves the cursor backward `count' bigwords.
1204 * If `count' is negative, the cursor is not moved. */
move_cursor_backward_bigword(int count)1205 void move_cursor_backward_bigword(int count)
1206 {
1207 if (alert_if_first())
1208 return;
1209
1210 size_t new_index = le_main_index;
1211 while (count-- > 0 && new_index > 0)
1212 new_index = previous_bigword_index(le_main_buffer.contents, new_index);
1213 exec_motion_command(new_index, false);
1214 }
1215
1216 /* Returns the index of the next bigword in string `s', counted from index `i'.
1217 * The return value is greater than `i' unless `s[i]' is a null character. */
1218 /* A bigword is a sequence of non-blank characters. */
next_bigword_index(const wchar_t * s,size_t i)1219 size_t next_bigword_index(const wchar_t *s, size_t i)
1220 {
1221 while (s[i] != L'\0' && !iswblank(s[i]))
1222 i++;
1223 while (s[i] != L'\0' && iswblank(s[i]))
1224 i++;
1225 return i;
1226 }
1227
1228 /* Returns the index of the end of the current bigword in string `s', counted
1229 * from index `i'. If `i' is at the end of the bigword and `progress' is true,
1230 * the end of the next bigword is returned.
1231 * The return value is greater than `i' unless `s[i]' is a null character. */
next_end_of_bigword_index(const wchar_t * s,size_t i,bool progress)1232 size_t next_end_of_bigword_index(const wchar_t *s, size_t i, bool progress)
1233 {
1234 const size_t init = i;
1235 start:
1236 if (s[i] == L'\0')
1237 return i;
1238 while (s[i] != L'\0' && iswblank(s[i]))
1239 i++;
1240 while (s[i] != L'\0' && !iswblank(s[i]))
1241 i++;
1242 i--;
1243 if (i > init || !progress) {
1244 return i;
1245 } else {
1246 i++;
1247 goto start;
1248 }
1249 }
1250
1251 /* Returns the index of the previous bigword in string `s', counted from index
1252 * `i'. The return value is less than `i' unless `i' is zero. */
previous_bigword_index(const wchar_t * s,size_t i)1253 size_t previous_bigword_index(const wchar_t *s, size_t i)
1254 {
1255 const size_t init = i;
1256 start:
1257 while (i > 0 && iswblank(s[i]))
1258 i--;
1259 while (i > 0 && !iswblank(s[i]))
1260 i--;
1261 if (i == 0)
1262 return i;
1263 i++;
1264 if (i < init) {
1265 return i;
1266 } else {
1267 i--;
1268 goto start;
1269 }
1270 }
1271
1272 /* Moves forward one semiword (or `count' semiwords if the count is set). */
1273 /* exclusive motion command */
cmd_forward_semiword(wchar_t c)1274 void cmd_forward_semiword(wchar_t c __attribute__((unused)))
1275 {
1276 int count = get_count(1);
1277 if (count >= 0)
1278 move_cursor_forward_semiword(count);
1279 else
1280 move_cursor_backward_semiword(-count);
1281 }
1282
1283 /* Moves the cursor to the end of the current semiword (or the next semiword if
1284 * already at the end). If the count is set, moves to the end of the `count'th
1285 * semiword. */
1286 /* inclusive motion command */
cmd_end_of_semiword(wchar_t c)1287 void cmd_end_of_semiword(wchar_t c __attribute__((unused)))
1288 {
1289 if (alert_if_last())
1290 return;
1291
1292 int count = get_count(1);
1293 size_t new_index = le_main_index;
1294 while (--count >= 0 && new_index < le_main_buffer.length)
1295 new_index = next_end_of_semiword_index(
1296 le_main_buffer.contents, new_index, true);
1297 exec_motion_command(new_index, true);
1298 }
1299
1300 /* Moves backward one semiword (or `count' semiwords if the count is set). */
1301 /* exclusive motion command */
cmd_backward_semiword(wchar_t c)1302 void cmd_backward_semiword(wchar_t c __attribute__((unused)))
1303 {
1304 int count = get_count(1);
1305 if (count >= 0)
1306 move_cursor_backward_semiword(count);
1307 else
1308 move_cursor_forward_semiword(-count);
1309 }
1310
1311 /* Moves the cursor forward `count' semiwords.
1312 * If `count' is negative, the cursor is not moved. */
move_cursor_forward_semiword(int count)1313 void move_cursor_forward_semiword(int count)
1314 {
1315 if (alert_if_last())
1316 return;
1317
1318 size_t new_index = le_main_index;
1319 if (!need_cw_treatment()) {
1320 while (count-- > 0 && new_index < le_main_buffer.length)
1321 new_index = next_semiword_index(le_main_buffer.contents, new_index);
1322 exec_motion_command(new_index, false);
1323 } else {
1324 while (count > 1 && new_index < le_main_buffer.length) {
1325 new_index = next_semiword_index(le_main_buffer.contents, new_index);
1326 count--;
1327 }
1328 if (count > 0 && new_index < le_main_buffer.length) {
1329 new_index = next_end_of_semiword_index(
1330 le_main_buffer.contents, new_index, false);
1331 }
1332 exec_motion_command(new_index, true);
1333 }
1334 }
1335
1336 /* Moves the cursor backward `count' semiwords.
1337 * If `count' is negative, the cursor is not moved. */
move_cursor_backward_semiword(int count)1338 void move_cursor_backward_semiword(int count)
1339 {
1340 if (alert_if_first())
1341 return;
1342
1343 size_t new_index = le_main_index;
1344 while (count-- > 0 && new_index > 0)
1345 new_index = previous_semiword_index(le_main_buffer.contents, new_index);
1346 exec_motion_command(new_index, false);
1347 }
1348
1349 /* Returns the index of the next semiword in string `s', counted from index `i'.
1350 * The return value is greater than `i' unless `s[i]' is a null character. */
1351 /* A "semiword" is a sequence of characters that are not <blank> or <punct>. */
next_semiword_index(const wchar_t * s,size_t i)1352 size_t next_semiword_index(const wchar_t *s, size_t i)
1353 {
1354 while (s[i] != L'\0' && !iswblank(s[i]) && !iswpunct(s[i]))
1355 i++;
1356 while (s[i] != L'\0' && (iswblank(s[i]) || iswpunct(s[i])))
1357 i++;
1358 return i;
1359 }
1360
1361 /* Returns the index of the end of the current semiword in string `s', counted
1362 * from index `i'. If `i' is at the end of the semiword and `progress' is true,
1363 * the end of the next semiword is returned.
1364 * The return value is greater than `i' unless `s[i]' is a null character. */
next_end_of_semiword_index(const wchar_t * s,size_t i,bool progress)1365 size_t next_end_of_semiword_index(const wchar_t *s, size_t i, bool progress)
1366 {
1367 const size_t init = i;
1368 start:
1369 if (s[i] == L'\0')
1370 return i;
1371 while (s[i] != L'\0' && (iswblank(s[i]) || iswpunct(s[i])))
1372 i++;
1373 while (s[i] != L'\0' && !iswblank(s[i]) && !iswpunct(s[i]))
1374 i++;
1375 i--;
1376 if (i > init || !progress) {
1377 return i;
1378 } else {
1379 i++;
1380 goto start;
1381 }
1382 }
1383
1384 /* Returns the index of the previous semiword in string `s', counted from index
1385 * `i'. The return value is less than `i' unless `i' is zero. */
previous_semiword_index(const wchar_t * s,size_t i)1386 size_t previous_semiword_index(const wchar_t *s, size_t i)
1387 {
1388 const size_t init = i;
1389 start:
1390 while (i > 0 && (iswblank(s[i]) || iswpunct(s[i])))
1391 i--;
1392 while (i > 0 && !iswblank(s[i]) && !iswpunct(s[i]))
1393 i--;
1394 if (i == 0)
1395 return i;
1396 i++;
1397 if (i < init) {
1398 return i;
1399 } else {
1400 i--;
1401 goto start;
1402 }
1403 }
1404
1405 /* Moves forward one viword (or `count' viwords if the count is set). */
1406 /* exclusive motion command */
cmd_forward_viword(wchar_t c)1407 void cmd_forward_viword(wchar_t c __attribute__((unused)))
1408 {
1409 int count = get_count(1);
1410 if (count >= 0)
1411 move_cursor_forward_viword(count);
1412 else
1413 move_cursor_backward_viword(-count);
1414 }
1415
1416 /* Moves the cursor to the end of the current viword (or the next viword if
1417 * already at the end). If the count is set, moves to the end of the `count'th
1418 * viword. */
1419 /* inclusive motion command */
cmd_end_of_viword(wchar_t c)1420 void cmd_end_of_viword(wchar_t c __attribute__((unused)))
1421 {
1422 if (alert_if_last())
1423 return;
1424
1425 int count = get_count(1);
1426 size_t new_index = le_main_index;
1427 while (--count >= 0 && new_index < le_main_buffer.length)
1428 new_index = next_end_of_viword_index(
1429 le_main_buffer.contents, new_index, true);
1430 exec_motion_command(new_index, true);
1431 }
1432
1433 /* Moves backward one viword (or `count' viwords if the count is set). */
1434 /* exclusive motion command */
cmd_backward_viword(wchar_t c)1435 void cmd_backward_viword(wchar_t c __attribute__((unused)))
1436 {
1437 int count = get_count(1);
1438 if (count >= 0)
1439 move_cursor_backward_viword(count);
1440 else
1441 move_cursor_forward_viword(-count);
1442 }
1443
1444 /* Moves the cursor forward `count' viwords.
1445 * If `count' is negative, the cursor is not moved. */
move_cursor_forward_viword(int count)1446 void move_cursor_forward_viword(int count)
1447 {
1448 if (alert_if_last())
1449 return;
1450
1451 size_t new_index = le_main_index;
1452 if (!need_cw_treatment()) {
1453 while (count-- > 0 && new_index < le_main_buffer.length)
1454 new_index = next_viword_index(le_main_buffer.contents, new_index);
1455 exec_motion_command(new_index, false);
1456 } else {
1457 while (count > 1 && new_index < le_main_buffer.length) {
1458 new_index = next_viword_index(le_main_buffer.contents, new_index);
1459 count--;
1460 }
1461 if (count > 0 && new_index < le_main_buffer.length) {
1462 new_index = next_end_of_viword_index(
1463 le_main_buffer.contents, new_index, false);
1464 }
1465 exec_motion_command(new_index, true);
1466 }
1467 }
1468
1469 /* Checks if we need a special treatment for the "cw" and "cW" commands. */
need_cw_treatment(void)1470 bool need_cw_treatment(void)
1471 {
1472 return (state.pending_command_motion & MEC_INSERT)
1473 && !iswblank(le_main_buffer.contents[le_main_index]);
1474 }
1475
1476 /* Moves the cursor backward `count' viwords.
1477 * If `count' is negative, the cursor is not moved. */
move_cursor_backward_viword(int count)1478 void move_cursor_backward_viword(int count)
1479 {
1480 if (alert_if_first())
1481 return;
1482
1483 size_t new_index = le_main_index;
1484 while (count-- > 0 && new_index > 0)
1485 new_index = previous_viword_index(le_main_buffer.contents, new_index);
1486 exec_motion_command(new_index, false);
1487 }
1488
1489 /* Returns the index of the next viword in string `s', counted from index `i'.
1490 * The return value is greater than `i' unless `s[i]' is a null character. */
1491 /* A viword is a sequence either of alphanumeric characters and underscores or
1492 * of other non-blank characters. */
next_viword_index(const wchar_t * s,size_t i)1493 size_t next_viword_index(const wchar_t *s, size_t i)
1494 {
1495 if (s[i] == L'_' || iswalnum(s[i])) {
1496 do
1497 i++;
1498 while (s[i] == L'_' || iswalnum(s[i]));
1499 if (!iswblank(s[i]))
1500 return i;
1501 } else {
1502 while (!iswblank(s[i])) {
1503 if (s[i] == L'\0')
1504 return i;
1505 i++;
1506 if (s[i] == L'_' || iswalnum(s[i]))
1507 return i;
1508 }
1509 }
1510
1511 do
1512 i++;
1513 while (iswblank(s[i]));
1514
1515 return i;
1516 }
1517
1518 /* Returns the index of the end of the current viword in string `s', counted
1519 * from index `i'.
1520 * If `progress' is true:
1521 * If `i' is at the end of the viword, the end of the next viword is returned.
1522 * The return value is greater than `i' unless `s[i]' is a null character.
1523 * If `progress' is false:
1524 * If `i' is at the end of the viword, `i' is returned. */
next_end_of_viword_index(const wchar_t * s,size_t i,bool progress)1525 size_t next_end_of_viword_index(const wchar_t *s, size_t i, bool progress)
1526 {
1527 const size_t init = i;
1528 start:
1529 while (iswblank(s[i]))
1530 i++;
1531 if (s[i] == L'\0')
1532 return i;
1533 if (s[i] == L'_' || iswalnum(s[i])) {
1534 do
1535 i++;
1536 while (s[i] == L'_' || iswalnum(s[i]));
1537 } else {
1538 do
1539 i++;
1540 while (s[i] != L'\0' && s[i] != L'_'
1541 && !iswblank(s[i]) && !iswalnum(s[i]));
1542 }
1543 i--;
1544 if (i > init || !progress) {
1545 return i;
1546 } else {
1547 i++;
1548 goto start;
1549 }
1550 }
1551
1552 /* Returns the index of the previous viword in string `s', counted form index
1553 * `i'. The return value is less than `i' unless `i' is zero. */
previous_viword_index(const wchar_t * s,size_t i)1554 size_t previous_viword_index(const wchar_t *s, size_t i)
1555 {
1556 const size_t init = i;
1557 start:
1558 while (i > 0 && iswblank(s[i]))
1559 i--;
1560 if (s[i] == L'_' || iswalnum(s[i])) {
1561 do {
1562 if (i == 0)
1563 return 0;
1564 i--;
1565 } while (s[i] == L'_' || iswalnum(s[i]));
1566 } else {
1567 do {
1568 if (i == 0)
1569 return 0;
1570 i--;
1571 } while (s[i] != L'_' && !iswblank(s[i]) && !iswalnum(s[i]));
1572 }
1573 i++;
1574 if (i < init) {
1575 return i;
1576 } else {
1577 i--;
1578 goto start;
1579 }
1580 }
1581
1582 /* Moves to the next emacsword (or the `count'th emacsword if the count is set).
1583 */
1584 /* exclusive motion command */
cmd_forward_emacsword(wchar_t c)1585 void cmd_forward_emacsword(wchar_t c __attribute__((unused)))
1586 {
1587 int count = get_count(1);
1588 if (count >= 0)
1589 move_cursor_forward_emacsword(count);
1590 else
1591 move_cursor_backward_emacsword(-count);
1592 }
1593
1594 /* Moves backward one emacsword (or `count' emacswords if the count is set). */
1595 /* exclusive motion command */
cmd_backward_emacsword(wchar_t c)1596 void cmd_backward_emacsword(wchar_t c __attribute__((unused)))
1597 {
1598 int count = get_count(1);
1599 if (count >= 0)
1600 move_cursor_backward_emacsword(count);
1601 else
1602 move_cursor_forward_emacsword(-count);
1603 }
1604
1605 /* Moves the cursor to the `count'th emacsword.
1606 * If `count' is negative, the cursor is not moved. */
move_cursor_forward_emacsword(int count)1607 void move_cursor_forward_emacsword(int count)
1608 {
1609 size_t new_index = le_main_index;
1610 while (count-- > 0 && new_index < le_main_buffer.length)
1611 new_index = next_emacsword_index(le_main_buffer.contents, new_index);
1612 exec_motion_command(new_index, false);
1613 }
1614
1615 /* Moves the cursor backward `count'th emacsword.
1616 * If `count' is negative, the cursor is not moved. */
move_cursor_backward_emacsword(int count)1617 void move_cursor_backward_emacsword(int count)
1618 {
1619 size_t new_index = le_main_index;
1620 while (count-- > 0 && new_index > 0)
1621 new_index = previous_emacsword_index(
1622 le_main_buffer.contents, new_index);
1623 exec_motion_command(new_index, false);
1624 }
1625
1626 /* Returns the index of the next emacsword in string `s', counted from index
1627 * `i'. The return value is greater than `i' unless `s[i]' is a null character.
1628 */
1629 /* An emacsword is a sequence of non-alphanumeric characters. */
next_emacsword_index(const wchar_t * s,size_t i)1630 size_t next_emacsword_index(const wchar_t *s, size_t i)
1631 {
1632 while (s[i] != L'\0' && !iswalnum(s[i]))
1633 i++;
1634 while (s[i] != L'\0' && iswalnum(s[i]))
1635 i++;
1636 return i;
1637 }
1638
1639 /* Returns the index of the previous emacsword in string `s', counted from
1640 * index `i'. The return value is less than `i' unless `i' is zero. */
1641 /* An emacsword is a sequence of alphanumeric characters. */
previous_emacsword_index(const wchar_t * s,size_t i)1642 size_t previous_emacsword_index(const wchar_t *s, size_t i)
1643 {
1644 const size_t init = i;
1645 start:
1646 while (i > 0 && !iswalnum(s[i]))
1647 i--;
1648 while (i > 0 && iswalnum(s[i]))
1649 i--;
1650 if (i == 0)
1651 return i;
1652 i++;
1653 if (i < init) {
1654 return i;
1655 } else {
1656 i--;
1657 goto start;
1658 }
1659 }
1660
1661 /* Moves the cursor to the beginning of the line. */
1662 /* exclusive motion command */
cmd_beginning_of_line(wchar_t c)1663 void cmd_beginning_of_line(wchar_t c __attribute__((unused)))
1664 {
1665 exec_motion_command(0, false);
1666 }
1667
1668 /* Moves the cursor to the end of the line. */
1669 /* inclusive motion command */
cmd_end_of_line(wchar_t c)1670 void cmd_end_of_line(wchar_t c __attribute__((unused)))
1671 {
1672 exec_motion_command(le_main_buffer.length, true);
1673 }
1674
1675 /* Moves the cursor to the `count'th character in the edit line.
1676 * If the count is not set, moves to the beginning of the line. If the count is
1677 * negative, moves to the `le_main_buffer.length + count'th character. */
1678 /* exclusive motion command */
cmd_go_to_column(wchar_t c)1679 void cmd_go_to_column(wchar_t c __attribute__((unused)))
1680 {
1681 int index = get_count(0);
1682
1683 if (index >= 0) {
1684 if (index > 0)
1685 index--;
1686 #if COUNT_ABS_MAX > SIZE_MAX
1687 if (index > (int) le_main_buffer.length)
1688 #else
1689 if ((size_t) index > le_main_buffer.length)
1690 #endif
1691 index = le_main_buffer.length;
1692 } else {
1693 #if COUNT_ABS_MAX > SIZE_MAX
1694 if (-index > (int) le_main_buffer.length)
1695 #else
1696 if ((size_t) -index > le_main_buffer.length)
1697 #endif
1698 index = 0;
1699 else
1700 index = (int) le_main_buffer.length + index;
1701 }
1702 exec_motion_command((size_t) index, false);
1703 }
1704
1705 /* Moves the cursor to the first non-blank character. */
1706 /* exclusive motion command */
cmd_first_nonblank(wchar_t c)1707 void cmd_first_nonblank(wchar_t c __attribute__((unused)))
1708 {
1709 size_t i = 0;
1710
1711 while (c = le_main_buffer.contents[i], c != L'\0' && iswblank(c))
1712 i++;
1713 exec_motion_command(i, false);
1714 }
1715
1716 /* Sets the editing mode to "char expect" and the pending command to
1717 * `find_char'. */
cmd_find_char(wchar_t c)1718 void cmd_find_char(wchar_t c __attribute__((unused)))
1719 {
1720 maybe_save_undo_history();
1721 set_char_expect_command(find_char);
1722 }
1723
1724 /* Moves the cursor to the `count'th occurrence of `c' after the current
1725 * position. */
1726 /* inclusive motion command */
find_char(wchar_t c)1727 void find_char(wchar_t c)
1728 {
1729 exec_find(c, get_count(1), false);
1730 }
1731
1732 /* Sets the editing mode to "char expect" and the pending command to
1733 * `find_char_rev'. */
cmd_find_char_rev(wchar_t c)1734 void cmd_find_char_rev(wchar_t c __attribute__((unused)))
1735 {
1736 maybe_save_undo_history();
1737 set_char_expect_command(find_char_rev);
1738 }
1739
1740 /* Moves the cursor to the `count'th occurrence of `c' before the current
1741 * position. */
1742 /* exclusive motion command */
find_char_rev(wchar_t c)1743 void find_char_rev(wchar_t c)
1744 {
1745 exec_find(c, -get_count(1), false);
1746 }
1747
1748 /* Sets the editing mode to "char expect" and the pending command to
1749 * `till_char'. */
cmd_till_char(wchar_t c)1750 void cmd_till_char(wchar_t c __attribute__((unused)))
1751 {
1752 maybe_save_undo_history();
1753 set_char_expect_command(till_char);
1754 }
1755
1756 /* Moves the cursor to the character just before `count'th occurrence of `c'
1757 * after the current position. */
1758 /* inclusive motion command */
till_char(wchar_t c)1759 void till_char(wchar_t c)
1760 {
1761 exec_find(c, get_count(1), true);
1762 }
1763
1764 /* Sets the editing mode to "char expect" and the pending command to
1765 * `till_char_rev'. */
cmd_till_char_rev(wchar_t c)1766 void cmd_till_char_rev(wchar_t c __attribute__((unused)))
1767 {
1768 maybe_save_undo_history();
1769 set_char_expect_command(till_char_rev);
1770 }
1771
1772 /* Moves the cursor to the character just after `count'th occurrence of `c'
1773 * before the current position. */
1774 /* exclusive motion command */
till_char_rev(wchar_t c)1775 void till_char_rev(wchar_t c)
1776 {
1777 exec_find(c, -get_count(1), true);
1778 }
1779
1780 /* Executes the find/till command. */
exec_find(wchar_t c,int count,bool till)1781 void exec_find(wchar_t c, int count, bool till)
1782 {
1783 le_set_mode(savemode);
1784 save_current_find_command();
1785
1786 size_t new_index = find_nth_occurence(c, count);
1787 if (new_index == SIZE_MAX)
1788 goto error;
1789 if (till) {
1790 if (new_index >= le_main_index) {
1791 if (new_index == 0)
1792 goto error;
1793 new_index--;
1794 } else {
1795 if (new_index == le_main_buffer.length)
1796 goto error;
1797 new_index++;
1798 }
1799 }
1800 exec_motion_command(new_index, new_index >= le_main_index);
1801 return;
1802
1803 error:
1804 cmd_alert(L'\0');
1805 return;
1806 }
1807
1808 /* Finds the position of the `n'th occurrence of `c' in the edit line from the
1809 * current position. Returns `SIZE_MAX' on failure (no such occurrence). */
find_nth_occurence(wchar_t c,int n)1810 size_t find_nth_occurence(wchar_t c, int n)
1811 {
1812 size_t i = le_main_index;
1813
1814 if (n == 0) {
1815 return i;
1816 } else if (c == L'\0') {
1817 return SIZE_MAX; /* no such occurrence */
1818 } else if (n >= 0) {
1819 while (n > 0 && i < le_main_buffer.length) {
1820 i++;
1821 if (le_main_buffer.contents[i] == c)
1822 n--;
1823 }
1824 } else {
1825 while (n < 0 && i > 0) {
1826 i--;
1827 if (le_main_buffer.contents[i] == c)
1828 n++;
1829 }
1830 }
1831 if (n != 0)
1832 return SIZE_MAX; /* no such occurrence */
1833 else
1834 return i;
1835 }
1836
1837 /* Redoes the last find/till command. */
cmd_refind_char(wchar_t c)1838 void cmd_refind_char(wchar_t c __attribute__((unused)))
1839 {
1840 if (!last_find_command.func) {
1841 cmd_alert(L'\0');
1842 return;
1843 }
1844
1845 last_find_command.func(last_find_command.arg);
1846 }
1847
1848 /* Redoes the last find/till command in the reverse direction. */
cmd_refind_char_rev(wchar_t c)1849 void cmd_refind_char_rev(wchar_t c __attribute__((unused)))
1850 {
1851 if (!last_find_command.func) {
1852 cmd_alert(L'\0');
1853 return;
1854 }
1855
1856 if (state.count.sign == 0)
1857 state.count.sign = -1, state.count.abs = 1;
1858 else if (state.count.sign >= 0)
1859 state.count.sign = -1;
1860 else
1861 state.count.sign = 1;
1862 last_find_command.func(last_find_command.arg);
1863 }
1864
1865
1866 /********** Editing Commands **********/
1867
1868 /* Removes the character under the cursor.
1869 * If the count is set, `count' characters are killed. */
cmd_delete_char(wchar_t c)1870 void cmd_delete_char(wchar_t c __attribute__((unused)))
1871 {
1872 enum motion_expect_command_T cmd;
1873
1874 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1875 exec_motion_expect_command(cmd, cmd_forward_char);
1876 }
1877
1878 /* Removes the bigword after the cursor.
1879 * If the count is set, `count' bigwords are killed.
1880 * If the cursor is at the end of the line, the terminal is alerted. */
cmd_delete_bigword(wchar_t c)1881 void cmd_delete_bigword(wchar_t c __attribute__((unused)))
1882 {
1883 enum motion_expect_command_T cmd;
1884
1885 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1886 exec_motion_expect_command(cmd, cmd_forward_bigword);
1887 }
1888
1889 /* Removes the semiword after the cursor.
1890 * If the count is set, `count' semiwords are killed.
1891 * If the cursor is at the end of the line, the terminal is alerted. */
cmd_delete_semiword(wchar_t c)1892 void cmd_delete_semiword(wchar_t c __attribute__((unused)))
1893 {
1894 enum motion_expect_command_T cmd;
1895
1896 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1897 exec_motion_expect_command(cmd, cmd_forward_semiword);
1898 }
1899
1900 /* Removes the viword after the cursor.
1901 * If the count is set, `count' viwords are killed.
1902 * If the cursor is at the end of the line, the terminal is alerted. */
cmd_delete_viword(wchar_t c)1903 void cmd_delete_viword(wchar_t c __attribute__((unused)))
1904 {
1905 enum motion_expect_command_T cmd;
1906
1907 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1908 exec_motion_expect_command(cmd, cmd_forward_viword);
1909 }
1910
1911 /* Removes the emacsword after the cursor.
1912 * If the count is set, `count' emacswords are killed.
1913 * If the cursor is at the end of the line, the terminal is alerted. */
cmd_delete_emacsword(wchar_t c)1914 void cmd_delete_emacsword(wchar_t c __attribute__((unused)))
1915 {
1916 enum motion_expect_command_T cmd;
1917
1918 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1919 exec_motion_expect_command(cmd, cmd_forward_emacsword);
1920 }
1921
1922 /* Removes the character behind the cursor.
1923 * If the count is set, `count' characters are killed. */
cmd_backward_delete_char(wchar_t c)1924 void cmd_backward_delete_char(wchar_t c __attribute__((unused)))
1925 {
1926 enum motion_expect_command_T cmd;
1927
1928 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1929 exec_motion_expect_command(cmd, cmd_backward_char);
1930 }
1931
1932 /* Removes the bigword behind the cursor.
1933 * If the count is set, `count' bigwords are killed.
1934 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_delete_bigword(wchar_t c)1935 void cmd_backward_delete_bigword(wchar_t c __attribute__((unused)))
1936 {
1937 enum motion_expect_command_T cmd;
1938
1939 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1940 exec_motion_expect_command(cmd, cmd_backward_bigword);
1941 }
1942
1943 /* Removes the semiword behind the cursor.
1944 * If the count is set, `count' semiwords are killed.
1945 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_delete_semiword(wchar_t c)1946 void cmd_backward_delete_semiword(wchar_t c __attribute__((unused)))
1947 {
1948 enum motion_expect_command_T cmd;
1949
1950 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1951 exec_motion_expect_command(cmd, cmd_backward_semiword);
1952 }
1953
1954 /* Removes the viword behind the cursor.
1955 * If the count is set, `count' viwords are killed.
1956 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_delete_viword(wchar_t c)1957 void cmd_backward_delete_viword(wchar_t c __attribute__((unused)))
1958 {
1959 enum motion_expect_command_T cmd;
1960
1961 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1962 exec_motion_expect_command(cmd, cmd_backward_viword);
1963 }
1964
1965 /* Removes the emacsword behind the cursor.
1966 * If the count is set, `count' emacswords are killed.
1967 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_delete_emacsword(wchar_t c)1968 void cmd_backward_delete_emacsword(wchar_t c __attribute__((unused)))
1969 {
1970 enum motion_expect_command_T cmd;
1971
1972 cmd = (state.count.sign == 0) ? MEC_DELETE : MEC_KILL;
1973 exec_motion_expect_command(cmd, cmd_backward_emacsword);
1974 }
1975
1976 /* Removes all characters in the edit line. */
cmd_delete_line(wchar_t c)1977 void cmd_delete_line(wchar_t c __attribute__((unused)))
1978 {
1979 exec_motion_expect_command_line(MEC_DELETE);
1980 }
1981
1982 /* Removes all characters after the cursor. */
cmd_forward_delete_line(wchar_t c)1983 void cmd_forward_delete_line(wchar_t c __attribute__((unused)))
1984 {
1985 exec_motion_expect_command(MEC_DELETE, cmd_end_of_line);
1986 }
1987
1988 /* Removes all characters behind the cursor. */
cmd_backward_delete_line(wchar_t c)1989 void cmd_backward_delete_line(wchar_t c __attribute__((unused)))
1990 {
1991 exec_motion_expect_command(MEC_DELETE, cmd_beginning_of_line);
1992 }
1993
1994 /* Kills the character under the cursor.
1995 * If the count is set, `count' characters are killed. */
cmd_kill_char(wchar_t c)1996 void cmd_kill_char(wchar_t c __attribute__((unused)))
1997 {
1998 exec_motion_expect_command(MEC_KILL, cmd_forward_char);
1999 }
2000
2001 /* Kills the bigword after the cursor.
2002 * If the count is set, `count' bigwords are killed.
2003 * If the cursor is at the end of the line, the terminal is alerted. */
cmd_kill_bigword(wchar_t c)2004 void cmd_kill_bigword(wchar_t c __attribute__((unused)))
2005 {
2006 exec_motion_expect_command(MEC_KILL, cmd_forward_bigword);
2007 }
2008
2009 /* Kills the semiword after the cursor.
2010 * If the count is set, `count' semiwords are killed.
2011 * If the cursor is at the end of the line, the terminal is alerted. */
cmd_kill_semiword(wchar_t c)2012 void cmd_kill_semiword(wchar_t c __attribute__((unused)))
2013 {
2014 exec_motion_expect_command(MEC_KILL, cmd_forward_semiword);
2015 }
2016
2017 /* Kills the viword after the cursor.
2018 * If the count is set, `count' viwords are killed.
2019 * If the cursor is at the end of the line, the terminal is alerted. */
cmd_kill_viword(wchar_t c)2020 void cmd_kill_viword(wchar_t c __attribute__((unused)))
2021 {
2022 exec_motion_expect_command(MEC_KILL, cmd_forward_viword);
2023 }
2024
2025 /* Kills the emacsword after the cursor.
2026 * If the count is set, `count' emacswords are killed.
2027 * If the cursor is at the end of the line, the terminal is alerted. */
cmd_kill_emacsword(wchar_t c)2028 void cmd_kill_emacsword(wchar_t c __attribute__((unused)))
2029 {
2030 exec_motion_expect_command(MEC_KILL, cmd_forward_emacsword);
2031 }
2032
2033 /* Kills the character behind the cursor.
2034 * If the count is set, `count' characters are killed.
2035 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_kill_char(wchar_t c)2036 void cmd_backward_kill_char(wchar_t c __attribute__((unused)))
2037 {
2038 exec_motion_expect_command(MEC_KILL, cmd_backward_char);
2039 }
2040
2041 /* Kills the bigword behind the cursor.
2042 * If the count is set, `count' bigwords are killed.
2043 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_kill_bigword(wchar_t c)2044 void cmd_backward_kill_bigword(wchar_t c __attribute__((unused)))
2045 {
2046 exec_motion_expect_command(MEC_KILL, cmd_backward_bigword);
2047 }
2048
2049 /* Kills the semiword behind the cursor.
2050 * If the count is set, `count' semiwords are killed.
2051 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_kill_semiword(wchar_t c)2052 void cmd_backward_kill_semiword(wchar_t c __attribute__((unused)))
2053 {
2054 exec_motion_expect_command(MEC_KILL, cmd_backward_semiword);
2055 }
2056
2057 /* Kills the viword behind the cursor.
2058 * If the count is set, `count' viwords are killed.
2059 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_kill_viword(wchar_t c)2060 void cmd_backward_kill_viword(wchar_t c __attribute__((unused)))
2061 {
2062 exec_motion_expect_command(MEC_KILL, cmd_backward_viword);
2063 }
2064
2065 /* Kills the emacsword behind the cursor.
2066 * If the count is set, `count' emacswords are killed.
2067 * If the cursor is at the beginning of the line, the terminal is alerted. */
cmd_backward_kill_emacsword(wchar_t c)2068 void cmd_backward_kill_emacsword(wchar_t c __attribute__((unused)))
2069 {
2070 exec_motion_expect_command(MEC_KILL, cmd_backward_emacsword);
2071 }
2072
2073 /* Kills all characters in the edit line. */
cmd_kill_line(wchar_t c)2074 void cmd_kill_line(wchar_t c __attribute__((unused)))
2075 {
2076 exec_motion_expect_command_line(MEC_KILL);
2077 }
2078
2079 /* Kills all characters after the cursor. */
cmd_forward_kill_line(wchar_t c)2080 void cmd_forward_kill_line(wchar_t c __attribute__((unused)))
2081 {
2082 exec_motion_expect_command(MEC_KILL, cmd_end_of_line);
2083 }
2084
2085 /* Kills all characters before the cursor. */
cmd_backward_kill_line(wchar_t c)2086 void cmd_backward_kill_line(wchar_t c __attribute__((unused)))
2087 {
2088 exec_motion_expect_command(MEC_KILL, cmd_beginning_of_line);
2089 }
2090
2091 /* Inserts the last-killed string before the cursor.
2092 * If the count is set, inserts `count' times.
2093 * The cursor is left on the last character inserted. */
cmd_put_before(wchar_t c)2094 void cmd_put_before(wchar_t c __attribute__((unused)))
2095 {
2096 put_killed_string(false, true);
2097 }
2098
2099 /* Inserts the last-killed string after the cursor.
2100 * If the count is set, inserts `count' times.
2101 * The cursor is left on the last character inserted. */
cmd_put(wchar_t c)2102 void cmd_put(wchar_t c __attribute__((unused)))
2103 {
2104 put_killed_string(true, true);
2105 }
2106
2107 /* Inserts the last-killed string before the cursor.
2108 * If the count is set, inserts `count' times.
2109 * The cursor is left after the inserted string. */
cmd_put_left(wchar_t c)2110 void cmd_put_left(wchar_t c __attribute__((unused)))
2111 {
2112 put_killed_string(false, false);
2113 }
2114
2115 /* Inserts the last-killed text at the current cursor position (`count' times).
2116 * If `after_cursor' is true, the text is inserted after the current cursor
2117 * position. Otherwise, before the current position.
2118 * If `cursor_on_last_char' is true, the cursor is left on the last character
2119 * inserted. Otherwise, the cursor is left after the inserted text. */
put_killed_string(bool after_cursor,bool cursor_on_last_char)2120 void put_killed_string(bool after_cursor, bool cursor_on_last_char)
2121 {
2122 ALERT_AND_RETURN_IF_PENDING;
2123 save_current_edit_command();
2124 maybe_save_undo_history();
2125
2126 size_t index = (next_kill_index - 1) % KILL_RING_SIZE;
2127 if (kill_ring[index] == NULL) {
2128 cmd_alert(L'\0');
2129 return;
2130 }
2131
2132 insert_killed_string(after_cursor, cursor_on_last_char, index);
2133 }
2134
2135 /* Inserts the killed text at the current cursor position (`count' times).
2136 * If `after_cursor' is true, the text is inserted after the current cursor
2137 * position. Otherwise, before the current position.
2138 * If `cursor_on_last_char' is true, the cursor is left on the last character
2139 * inserted. Otherwise, the cursor is left after the inserted text.
2140 * `index' specifies the text in the kill ring to be inserted. If the text
2141 * does not exist at the specified index in the kill ring, this function does
2142 * nothing. */
insert_killed_string(bool after_cursor,bool cursor_on_last_char,size_t index)2143 void insert_killed_string(
2144 bool after_cursor, bool cursor_on_last_char, size_t index)
2145 {
2146 clear_prediction();
2147
2148 const wchar_t *s = kill_ring[index];
2149 if (s == NULL)
2150 return;
2151
2152 last_put_elem = index;
2153 if (after_cursor && le_main_index < le_main_buffer.length)
2154 le_main_index++;
2155
2156 size_t offset = le_main_buffer.length - le_main_index;
2157 for (int count = get_count(1); --count >= 0; )
2158 wb_insert(&le_main_buffer, le_main_index, s);
2159 assert(le_main_buffer.length >= offset + 1);
2160
2161 last_put_range_start = le_main_index;
2162 le_main_index = le_main_buffer.length - offset;
2163 last_put_range_length = le_main_index - last_put_range_start;
2164 if (cursor_on_last_char)
2165 le_main_index--;
2166
2167 reset_state();
2168 }
2169
2170 /* Replaces the string just inserted by `cmd_put_left' with the previously
2171 * killed string. */
cmd_put_pop(wchar_t c)2172 void cmd_put_pop(wchar_t c __attribute__((unused)))
2173 {
2174 static bool last_success = false;
2175
2176 ALERT_AND_RETURN_IF_PENDING;
2177 if ((last_command.func != cmd_put_left
2178 && last_command.func != cmd_put
2179 && last_command.func != cmd_put_before
2180 && (last_command.func != cmd_put_pop || !last_success))
2181 || kill_ring[last_put_elem] == NULL) {
2182 last_success = false;
2183 cmd_alert(L'\0');
2184 return;
2185 }
2186 last_success = true;
2187 save_current_edit_command();
2188 maybe_save_undo_history();
2189 clear_prediction();
2190
2191 size_t index = last_put_elem;
2192 do
2193 index = (index - 1) % KILL_RING_SIZE;
2194 while (kill_ring[index] == NULL);
2195
2196 /* Remove the just inserted text. */
2197 assert(last_put_range_start <= le_main_buffer.length);
2198 wb_remove(&le_main_buffer, last_put_range_start, last_put_range_length);
2199 le_main_index = last_put_range_start;
2200
2201 insert_killed_string(false, false, index);
2202 }
2203
2204 /* Undoes the last editing command. */
cmd_undo(wchar_t c)2205 void cmd_undo(wchar_t c __attribute__((unused)))
2206 {
2207 cancel_undo(-get_count(1));
2208 }
2209
2210 /* Undoes all changes to the edit line. */
cmd_undo_all(wchar_t c)2211 void cmd_undo_all(wchar_t c __attribute__((unused)))
2212 {
2213 cancel_undo(-COUNT_ABS_MAX);
2214 }
2215
2216 /* Cancels the last undo. */
cmd_cancel_undo(wchar_t c)2217 void cmd_cancel_undo(wchar_t c __attribute__((unused)))
2218 {
2219 cancel_undo(get_count(1));
2220 }
2221
2222 /* Cancels all previous undo. */
cmd_cancel_undo_all(wchar_t c)2223 void cmd_cancel_undo_all(wchar_t c __attribute__((unused)))
2224 {
2225 cancel_undo(COUNT_ABS_MAX);
2226 }
2227
2228 /* Performs "undo"/"cancel undo".
2229 * `undo_index' is increased by `offset' and the contents of the history entry
2230 * of the new index is set to the edit line.
2231 * `offset' must be between `-COUNT_ABS_MAX' and `COUNT_ABS_MAX'. */
cancel_undo(int offset)2232 void cancel_undo(int offset)
2233 {
2234 maybe_save_undo_history();
2235 clear_prediction();
2236
2237 if (undo_history_entry != main_history_entry)
2238 goto error;
2239 if (offset < 0) {
2240 if (undo_index == 0)
2241 goto error;
2242 #if COUNT_ABS_MAX > SIZE_MAX
2243 if (-offset > (int) undo_index)
2244 #else
2245 if ((size_t) -offset > undo_index)
2246 #endif
2247 undo_index = 0;
2248 else
2249 undo_index += offset;
2250 } else {
2251 if (undo_index + 1 >= undo_history.length)
2252 goto error;
2253 #if COUNT_ABS_MAX > SIZE_MAX
2254 if (offset >= (int) (undo_history.length - undo_index))
2255 #else
2256 if ((size_t) offset >= undo_history.length - undo_index)
2257 #endif
2258 undo_index = undo_history.length - 1;
2259 else
2260 undo_index += offset;
2261 }
2262
2263 const struct undo_history *entry = undo_history.contents[undo_index];
2264 wb_replace(&le_main_buffer, 0, SIZE_MAX, entry->contents, SIZE_MAX);
2265 assert(entry->index <= le_main_buffer.length);
2266 le_main_index = entry->index;
2267
2268 reset_state();
2269 return;
2270
2271 error:
2272 cmd_alert(L'\0');
2273 return;
2274 }
2275
2276 /* Redoes the last editing command. */
2277 /* XXX: currently vi's "i" command cannot be redone. */
cmd_redo(wchar_t c)2278 void cmd_redo(wchar_t c __attribute__((unused)))
2279 {
2280 ALERT_AND_RETURN_IF_PENDING;
2281 if (!last_edit_command.command.func) {
2282 cmd_alert(L'\0');
2283 return;
2284 }
2285
2286 if (state.count.sign != 0)
2287 last_edit_command.state.count = state.count;
2288 state = last_edit_command.state;
2289 last_edit_command.command.func(last_edit_command.command.arg);
2290 }
2291
2292
2293 /********** Completion Commands **********/
2294
2295 /* Performs command line completion. */
cmd_complete(wchar_t c)2296 void cmd_complete(wchar_t c __attribute__((unused)))
2297 {
2298 ALERT_AND_RETURN_IF_PENDING;
2299 clear_prediction();
2300 check_reset_completion();
2301
2302 le_complete(lecr_normal);
2303
2304 reset_state();
2305 }
2306
2307 /* Selects the next completion candidate.
2308 * If the count is set, selects the `count'th next candidate. */
cmd_complete_next_candidate(wchar_t c)2309 void cmd_complete_next_candidate(wchar_t c __attribute__((unused)))
2310 {
2311 ALERT_AND_RETURN_IF_PENDING;
2312 clear_prediction();
2313 check_reset_completion();
2314
2315 le_complete_select_candidate(get_count(1));
2316
2317 reset_state();
2318 }
2319
2320 /* Selects the previous completion candidate.
2321 * If the count is set, selects the `count'th previous candidate. */
cmd_complete_prev_candidate(wchar_t c)2322 void cmd_complete_prev_candidate(wchar_t c __attribute__((unused)))
2323 {
2324 ALERT_AND_RETURN_IF_PENDING;
2325 clear_prediction();
2326 check_reset_completion();
2327
2328 le_complete_select_candidate(-get_count(1));
2329
2330 reset_state();
2331 }
2332
2333 /* Selects the first candidate in the next column.
2334 * If the count is set, selects that of the `count'th next column. */
cmd_complete_next_column(wchar_t c)2335 void cmd_complete_next_column(wchar_t c __attribute__((unused)))
2336 {
2337 ALERT_AND_RETURN_IF_PENDING;
2338 clear_prediction();
2339 check_reset_completion();
2340
2341 le_complete_select_column(get_count(1));
2342
2343 reset_state();
2344 }
2345
2346 /* Selects the first candidate in the previous column.
2347 * If the count is set, selects that of the `count'th previous column. */
cmd_complete_prev_column(wchar_t c)2348 void cmd_complete_prev_column(wchar_t c __attribute__((unused)))
2349 {
2350 ALERT_AND_RETURN_IF_PENDING;
2351 clear_prediction();
2352 check_reset_completion();
2353
2354 le_complete_select_column(-get_count(1));
2355
2356 reset_state();
2357 }
2358
2359 /* Selects the first candidate in the next page.
2360 * If the count is set, selects that of the `count'th next page. */
cmd_complete_next_page(wchar_t c)2361 void cmd_complete_next_page(wchar_t c __attribute__((unused)))
2362 {
2363 ALERT_AND_RETURN_IF_PENDING;
2364 clear_prediction();
2365 check_reset_completion();
2366
2367 le_complete_select_page(get_count(1));
2368
2369 reset_state();
2370 }
2371
2372 /* Selects the first candidate in the previous page.
2373 * If the count is set, selects that of the `count'th previous page. */
cmd_complete_prev_page(wchar_t c)2374 void cmd_complete_prev_page(wchar_t c __attribute__((unused)))
2375 {
2376 ALERT_AND_RETURN_IF_PENDING;
2377 clear_prediction();
2378 check_reset_completion();
2379
2380 le_complete_select_page(-get_count(1));
2381
2382 reset_state();
2383 }
2384
2385 /* Performs command line completion and
2386 * * if the count is not set, list all the candidates without changing the
2387 * main buffer.
2388 * * if the count is set, complete the `count'th candidate. */
cmd_complete_list(wchar_t c)2389 void cmd_complete_list(wchar_t c __attribute__((unused)))
2390 {
2391 ALERT_AND_RETURN_IF_PENDING;
2392 maybe_save_undo_history();
2393 clear_prediction();
2394 le_complete_cleanup();
2395 /* leave `next_reset_completion' to be true because the results of this
2396 * command cannot be used by succeeding completion commands. */
2397 // next_reset_completion = false;
2398
2399 le_complete_fix_candidate(get_count(0));
2400
2401 reset_state();
2402 }
2403
2404 /* Performs command line completion and replaces the current word with all of
2405 * the generated candidates. */
cmd_complete_all(wchar_t c)2406 void cmd_complete_all(wchar_t c __attribute__((unused)))
2407 {
2408 ALERT_AND_RETURN_IF_PENDING;
2409 clear_prediction();
2410 check_reset_completion();
2411
2412 le_complete(lecr_substitute_all_candidates);
2413
2414 reset_state();
2415 }
2416
2417 /* Performs command line completion and replaces the current word with the
2418 * longest common prefix of the candidates. */
cmd_complete_max(wchar_t c)2419 void cmd_complete_max(wchar_t c __attribute__((unused)))
2420 {
2421 ALERT_AND_RETURN_IF_PENDING;
2422 clear_prediction();
2423 check_reset_completion();
2424
2425 le_complete(lecr_longest_common_prefix);
2426
2427 reset_state();
2428 }
2429
2430 /* Like `cmd_complete_max' for a first key stroke, then like `cmd_complete'. */
cmd_complete_max_then_list(wchar_t c)2431 void cmd_complete_max_then_list(wchar_t c __attribute__((unused)))
2432 {
2433 ALERT_AND_RETURN_IF_PENDING;
2434 clear_prediction();
2435 check_reset_completion();
2436
2437 le_complete(last_command.func != cmd_complete_max_then_list
2438 ? lecr_longest_common_prefix : lecr_normal);
2439
2440 reset_state();
2441 }
2442
2443 /* Like `cmd_complete_max' for a first key stroke, then like
2444 * `cmd_complete_next_candidate'. */
cmd_complete_max_then_next_candidate(wchar_t c)2445 void cmd_complete_max_then_next_candidate(wchar_t c __attribute__((unused)))
2446 {
2447 ALERT_AND_RETURN_IF_PENDING;
2448 clear_prediction();
2449 check_reset_completion();
2450
2451 if (last_command.func != cmd_complete_max_then_next_candidate)
2452 le_complete(lecr_longest_common_prefix);
2453 else
2454 le_complete_select_candidate(get_count(1));
2455
2456 reset_state();
2457 }
2458
2459 /* Like `cmd_complete_max' for a first key stroke, then like
2460 * `cmd_complete_prev_candidate'. */
cmd_complete_max_then_prev_candidate(wchar_t c)2461 void cmd_complete_max_then_prev_candidate(wchar_t c __attribute__((unused)))
2462 {
2463 ALERT_AND_RETURN_IF_PENDING;
2464 clear_prediction();
2465 check_reset_completion();
2466
2467 if (last_command.func != cmd_complete_max_then_prev_candidate)
2468 le_complete(lecr_longest_common_prefix);
2469 else
2470 le_complete_select_candidate(-get_count(1));
2471
2472 reset_state();
2473 }
2474
2475 /* Clears the current candidates. */
cmd_clear_candidates(wchar_t c)2476 void cmd_clear_candidates(wchar_t c __attribute__((unused)))
2477 {
2478 le_complete_cleanup();
2479 }
2480
check_reset_completion(void)2481 void check_reset_completion(void)
2482 {
2483 if (reset_completion) {
2484 maybe_save_undo_history();
2485 le_complete_cleanup();
2486 reset_completion = false;
2487 }
2488 next_reset_completion = false;
2489 }
2490
2491
2492 /********** Prediction Commands **********/
2493
2494 #ifndef MAX_PREDICTION_SAMPLE
2495 #define MAX_PREDICTION_SAMPLE 10000
2496 #endif /* ifndef MAX_PREDICTION_SAMPLE */
2497
2498 /* Create a probability distribution tree for command prediction based on the
2499 * current history. The result is set to `prediction_tree'. */
create_prediction_tree(void)2500 void create_prediction_tree(void)
2501 {
2502 trie_T *t = trie_create();
2503 #define N 4
2504 size_t hits[N] = {0};
2505 for (const histlink_T *l = Histlist; (l = l->prev) != Histlist; ) {
2506 const histentry_T *e = (const histentry_T *) l;
2507 size_t k = count_matching_previous_commands(e);
2508 assert(k < N);
2509 for (size_t i = 0; i <= k; i++)
2510 hits[i]++;
2511 if (hits[0] >= MAX_PREDICTION_SAMPLE)
2512 break;
2513
2514 wchar_t *cmd = malloc_mbstowcs(e->value);
2515 if (cmd == NULL)
2516 continue;
2517 t = trie_add_probability(t, cmd, 1.0 / (hits[k] + 1));
2518 free(cmd);
2519 }
2520
2521 prediction_tree = t;
2522 }
2523
2524 // Counts N-1 at most
count_matching_previous_commands(const histentry_T * e)2525 size_t count_matching_previous_commands(const histentry_T *e)
2526 {
2527 size_t count = 0;
2528 const histlink_T *l1 = &e->link, *l2 = Histlist;
2529 while ((l1 = l1->prev) != Histlist) {
2530 l2 = l2->prev;
2531
2532 const histentry_T *e1 = (const histentry_T *) l1;
2533 const histentry_T *e2 = (const histentry_T *) l2;
2534 if (strcmp(e1->value, e2->value) != 0)
2535 break;
2536 count++;
2537 if (count >= N - 1)
2538 break;
2539 }
2540 return count;
2541 }
2542 #undef N
2543
2544 /* Clears the second part of `le_main_buffer'.
2545 * Commands that modify the buffer usually need to call this function. However,
2546 * if a command affects or is affected by the second part, the command might
2547 * need to update `le_main_length' in a more sophisticated way rather than
2548 * calling this function. */
clear_prediction(void)2549 void clear_prediction(void)
2550 {
2551 if (le_main_length < le_main_buffer.length)
2552 wb_truncate(&le_main_buffer, le_main_length);
2553 le_main_length = SIZE_MAX;
2554 }
2555
2556 /* Removes any existing prediction and, if the cursor is at the end of line,
2557 * appends a new prediction to the main buffer. */
update_buffer_with_prediction(void)2558 void update_buffer_with_prediction(void)
2559 {
2560 clear_prediction();
2561
2562 if (!shopt_le_predictempty && active_length() == 0)
2563 return;
2564
2565 if (le_main_index < active_length())
2566 return;
2567
2568 le_main_length = le_main_buffer.length;
2569
2570 wchar_t *suffix = trie_probable_key(
2571 prediction_tree, le_main_buffer.contents);
2572 wb_catfree(&le_main_buffer, suffix);
2573 }
2574
2575
2576 /********** Vi-Mode Specific Commands **********/
2577
2578 /* Sets the editing mode to "vi expect" and the pending command to
2579 * `vi_replace_char'. */
cmd_vi_replace_char(wchar_t c)2580 void cmd_vi_replace_char(wchar_t c __attribute__((unused)))
2581 {
2582 ALERT_AND_RETURN_IF_PENDING;
2583 set_char_expect_command(vi_replace_char);
2584 }
2585
2586 /* Replaces the character under the cursor with `c'.
2587 * If the count is set, the `count' characters are replaced. */
vi_replace_char(wchar_t c)2588 void vi_replace_char(wchar_t c)
2589 {
2590 save_current_edit_command();
2591 le_set_mode(savemode);
2592
2593 if (c != L'\0') {
2594 int count = get_count(1);
2595
2596 if (count > 0 && le_main_index < le_main_buffer.length) {
2597 do {
2598 le_main_buffer.contents[le_main_index] = c;
2599 count--, le_main_index++;
2600 } while (count > 0 && le_main_index < le_main_buffer.length);
2601 if (le_main_length < le_main_index)
2602 le_main_length = le_main_index;
2603 clear_prediction();
2604 le_main_index--;
2605 }
2606 reset_state();
2607 } else {
2608 cmd_alert(L'\0');
2609 }
2610 }
2611
2612 /* Moves the cursor to the beginning of the line and sets the editing mode to
2613 * "vi insert". */
cmd_vi_insert_beginning(wchar_t c)2614 void cmd_vi_insert_beginning(wchar_t c __attribute__((unused)))
2615 {
2616 exec_motion_expect_command_line(MEC_INSERT | MEC_TOSTART);
2617 }
2618
2619 /* Moves the cursor forward one character and sets the editing mode to "vi
2620 * insert". */
cmd_vi_append(wchar_t c)2621 void cmd_vi_append(wchar_t c __attribute__((unused)))
2622 {
2623 reset_count();
2624 exec_motion_expect_command(MEC_INSERT | MEC_MOVE, cmd_forward_char);
2625 }
2626
2627 /* Moves the cursor to the end of the line and sets the editing mode to "vi
2628 * insert".*/
cmd_vi_append_to_eol(wchar_t c)2629 void cmd_vi_append_to_eol(wchar_t c __attribute__((unused)))
2630 {
2631 exec_motion_expect_command_line(MEC_INSERT | MEC_TOEND);
2632 }
2633
2634 /* Sets the editing mode to "vi insert" and starts the overwrite mode. */
cmd_vi_replace(wchar_t c)2635 void cmd_vi_replace(wchar_t c __attribute__((unused)))
2636 {
2637 set_mode(LE_MODE_VI_INSERT, true);
2638 }
2639
2640 /* Sets the pending command to MEC_SWITCHCASE.
2641 * The count multiplier is set to the current count.
2642 * If the pending command is already set to MEC_SWITCHCASE, the whole line is
2643 * switch-cased. */
cmd_vi_switch_case(wchar_t c)2644 void cmd_vi_switch_case(wchar_t c __attribute__((unused)))
2645 {
2646 set_motion_expect_command(MEC_SWITCHCASE | MEC_TOSTART);
2647 }
2648
2649 /* Switches the case of the character under the cursor and advances the cursor.
2650 * If the count is set, `count' characters are changed. */
cmd_vi_switch_case_char(wchar_t c)2651 void cmd_vi_switch_case_char(wchar_t c __attribute__((unused)))
2652 {
2653 exec_motion_expect_command(MEC_SWITCHCASE | MEC_TOEND, cmd_forward_char);
2654 }
2655
2656 /* Sets the pending command to MEC_COPY.
2657 * The count multiplier is set to the current count.
2658 * If the pending command is already set to MEC_COPY, the whole line is copied
2659 * to the kill ring. */
cmd_vi_yank(wchar_t c)2660 void cmd_vi_yank(wchar_t c __attribute__((unused)))
2661 {
2662 set_motion_expect_command(MEC_COPY);
2663 }
2664
2665 /* Copies the content of the edit line from the current position to the end. */
cmd_vi_yank_to_eol(wchar_t c)2666 void cmd_vi_yank_to_eol(wchar_t c __attribute__((unused)))
2667 {
2668 exec_motion_expect_command(MEC_COPY, cmd_end_of_line);
2669 }
2670
2671 /* Sets the pending command to MEC_KILL.
2672 * The count multiplier is set to the current count.
2673 * If the pending command is already set to MEC_KILL, the whole line is moved
2674 * to the kill ring. */
cmd_vi_delete(wchar_t c)2675 void cmd_vi_delete(wchar_t c __attribute__((unused)))
2676 {
2677 set_motion_expect_command(MEC_KILL);
2678 }
2679
2680 /* Deletes the content of the edit line from the current position to the end and
2681 * put it in the kill ring. */
2682 /* cmd_vi_delete_to_eol is the same as cmd_forward_kill_line.
2683 void cmd_vi_delete_to_eol(wchar_t c __attribute__((unused)))
2684 {
2685 exec_motion_expect_command(MEC_KILL, cmd_end_of_line);
2686 }
2687 */
2688
2689 /* Sets the pending command to MEC_CHANGE.
2690 * The count multiplier is set to the current count.
2691 * If the pending command is already set to MEC_CHANGE, the whole line is
2692 * deleted and the editing mode is set to "vi insert". */
cmd_vi_change(wchar_t c)2693 void cmd_vi_change(wchar_t c __attribute__((unused)))
2694 {
2695 set_motion_expect_command(MEC_CHANGE);
2696 }
2697
2698 /* Deletes the content of the edit line from the current position to the end and
2699 * sets the editing mode to "vi insert". */
cmd_vi_change_to_eol(wchar_t c)2700 void cmd_vi_change_to_eol(wchar_t c __attribute__((unused)))
2701 {
2702 exec_motion_expect_command(MEC_CHANGE, cmd_end_of_line);
2703 }
2704
2705 /* Deletes all the content of the edit line and sets the editing mode to "vi
2706 * insert". */
cmd_vi_change_line(wchar_t c)2707 void cmd_vi_change_line(wchar_t c __attribute__((unused)))
2708 {
2709 exec_motion_expect_command_line(MEC_CHANGE);
2710 }
2711
2712 /* Sets the pending command to `MEC_COPYCHANGE'.
2713 * The count multiplier is set to the current count.
2714 * If the pending command is already set to `MEC_COPYCHANGE', the whole line is
2715 * moved to the kill ring and the editing mode is set to "vi insert". */
cmd_vi_yank_and_change(wchar_t c)2716 void cmd_vi_yank_and_change(wchar_t c __attribute__((unused)))
2717 {
2718 set_motion_expect_command(MEC_COPYCHANGE);
2719 }
2720
2721 /* Deletes the content of the edit line from the current position to the end,
2722 * put it in the kill ring, and sets the editing mode to "vi insert". */
cmd_vi_yank_and_change_to_eol(wchar_t c)2723 void cmd_vi_yank_and_change_to_eol(wchar_t c __attribute__((unused)))
2724 {
2725 exec_motion_expect_command(MEC_COPYCHANGE, cmd_end_of_line);
2726 }
2727
2728 /* Deletes all the content of the edit line, put it in the kill ring, and sets
2729 * the editing mode to "vi insert". */
cmd_vi_yank_and_change_line(wchar_t c)2730 void cmd_vi_yank_and_change_line(wchar_t c __attribute__((unused)))
2731 {
2732 exec_motion_expect_command_line(MEC_COPYCHANGE);
2733 }
2734
2735 /* Kills the character under the cursor and sets the editing mode to
2736 * "vi insert". If the count is set, `count' characters are killed. */
cmd_vi_substitute(wchar_t c)2737 void cmd_vi_substitute(wchar_t c __attribute__((unused)))
2738 {
2739 exec_motion_expect_command(MEC_COPYCHANGE, cmd_forward_char);
2740 }
2741
2742 /* Appends a space followed by the last bigword from the newest history entry.
2743 * If the count is specified, the `count'th word is appended.
2744 * The mode is changed to vi-insert. */
cmd_vi_append_last_bigword(wchar_t c)2745 void cmd_vi_append_last_bigword(wchar_t c __attribute__((unused)))
2746 {
2747 ALERT_AND_RETURN_IF_PENDING;
2748 save_current_edit_command();
2749 maybe_save_undo_history();
2750
2751 wchar_t *lastcmd = NULL;
2752 int count = get_count(-1);
2753 if (count == 0 || histlist.count == 0)
2754 goto fail;
2755
2756 struct xwcsrange range;
2757 lastcmd = malloc_mbstowcs(ashistentry(histlist.Newest)->value);
2758 if (lastcmd == NULL)
2759 goto fail;
2760 if (count >= 0) {
2761 /* find the count'th word */
2762 range.start = range.end = lastcmd;
2763 do {
2764 struct xwcsrange r = get_next_bigword(range.end);
2765 if (r.start == r.end)
2766 break;
2767 range = r;
2768 } while (--count > 0 && *range.end != L'\0');
2769 } else {
2770 /* find the count'th last word */
2771 range.start = range.end = lastcmd + wcslen(lastcmd);
2772 do {
2773 struct xwcsrange r = get_prev_bigword(lastcmd, range.start);
2774 if (r.start == r.end)
2775 break;
2776 range = r;
2777 } while (++count < 0 && lastcmd < range.start);
2778 }
2779 assert(range.start <= range.end);
2780 if (range.start == range.end)
2781 goto fail;
2782
2783 clear_prediction();
2784 if (le_main_index < le_main_buffer.length)
2785 le_main_index++;
2786 size_t len = range.end - range.start;
2787 wb_ninsert_force(&le_main_buffer, le_main_index, L" ", 1);
2788 le_main_index += 1;
2789 wb_ninsert_force(&le_main_buffer, le_main_index, range.start, len);
2790 le_main_index += len;
2791 free(lastcmd);
2792 cmd_setmode_viinsert(L'\0');
2793 return;
2794
2795 fail:
2796 free(lastcmd);
2797 cmd_alert(L'\0');
2798 return;
2799 }
2800
get_next_bigword(const wchar_t * s)2801 struct xwcsrange get_next_bigword(const wchar_t *s)
2802 {
2803 struct xwcsrange result;
2804 while (iswblank(*s))
2805 s++;
2806 result.start = s;
2807 while (*s != L'\0' && !iswblank(*s))
2808 s++;
2809 result.end = s;
2810 return result;
2811 /* result.start == result.end if no bigword found */
2812 }
2813
get_prev_bigword(const wchar_t * beginning,const wchar_t * s)2814 struct xwcsrange get_prev_bigword(const wchar_t *beginning, const wchar_t *s)
2815 {
2816 struct xwcsrange result;
2817 assert(beginning <= s);
2818 do {
2819 if (beginning == s) {
2820 result.start = result.end = s;
2821 return result;
2822 }
2823 } while (iswblank(*--s));
2824 result.end = &s[1];
2825 do {
2826 if (beginning == s) {
2827 result.start = s;
2828 return result;
2829 }
2830 } while (!iswblank(*--s));
2831 result.start = &s[1];
2832 return result;
2833 /* result.start == result.end if no bigword found */
2834 }
2835
2836 /* Sets the editing mode to "vi expect" and the pending command to
2837 * `vi_exec_alias'. */
cmd_vi_exec_alias(wchar_t c)2838 void cmd_vi_exec_alias(wchar_t c __attribute__((unused)))
2839 {
2840 ALERT_AND_RETURN_IF_PENDING;
2841 set_char_expect_command(vi_exec_alias);
2842 }
2843
2844 /* Appends the value of the alias `_c' to the pre-buffer so that the alias value
2845 * is interpreted as commands, where `c' in the alias name is the argument of
2846 * this command. */
vi_exec_alias(wchar_t c)2847 void vi_exec_alias(wchar_t c)
2848 {
2849 le_set_mode(savemode);
2850 state.pending_command_char = 0;
2851
2852 wchar_t aliasname[3] = { L'_', c, L'\0', };
2853 const wchar_t *aliasvalue = get_alias_value(aliasname);
2854 if (aliasvalue != NULL) {
2855 char *mbaliasvalue = malloc_wcstombs(aliasvalue);
2856 if (mbaliasvalue != NULL) {
2857 le_append_to_prebuffer(mbaliasvalue);
2858 return;
2859 }
2860 }
2861 cmd_alert(L'\0');
2862 }
2863
2864 /* Invokes an external command to edit the current line and accepts the result.
2865 * If the count is set, goes to the `count'th history entry and edit it.
2866 * If the editor returns a non-zero status, the line is not accepted. */
2867 /* cf. history.c:fc_edit_and_exec_entries */
cmd_vi_edit_and_accept(wchar_t c)2868 void cmd_vi_edit_and_accept(wchar_t c __attribute__((unused)))
2869 {
2870 ALERT_AND_RETURN_IF_PENDING;
2871
2872 char *tempfile;
2873 int fd;
2874 FILE *f;
2875 pid_t cpid;
2876 int savelaststatus;
2877
2878 if (state.count.sign != 0) {
2879 int num = get_count(0);
2880 if (num < 0)
2881 goto error0;
2882 const histlink_T *l = get_history_entry((unsigned) num);
2883 if (l == Histlist)
2884 goto error0;
2885 go_to_history(l, SEARCH_VI);
2886 }
2887 clear_prediction();
2888 le_complete_cleanup();
2889 le_suspend_readline();
2890
2891 fd = create_temporary_file(&tempfile, ".sh", S_IRUSR | S_IWUSR);
2892 if (fd < 0) {
2893 xerror(errno, Ngt("cannot create a temporary file to edit history"));
2894 goto error1;
2895 }
2896 f = fdopen(fd, "w");
2897 if (f == NULL) {
2898 xerror(errno, Ngt("cannot open temporary file `%s'"), tempfile);
2899 xclose(fd);
2900 goto error2;
2901 }
2902
2903 savelaststatus = laststatus;
2904 cpid = fork_and_reset(0, true, 0);
2905 if (cpid < 0) { // fork failed
2906 xerror(0, Ngt("cannot invoke the editor to edit history"));
2907 fclose(f);
2908 if (unlink(tempfile) < 0)
2909 xerror(errno, Ngt("failed to remove temporary file `%s'"),
2910 tempfile);
2911 error2:
2912 free(tempfile);
2913 error1:
2914 le_resume_readline();
2915 error0:
2916 cmd_alert(L'\0');
2917 } else if (cpid > 0) { // parent process
2918 fclose(f);
2919
2920 wchar_t **namep = wait_for_child(cpid,
2921 doing_job_control_now ? cpid : 0,
2922 doing_job_control_now);
2923 if (namep)
2924 *namep = malloc_wprintf(L"vi %s", tempfile);
2925 if (laststatus != Exit_SUCCESS)
2926 goto end;
2927
2928 f = fopen(tempfile, "r");
2929 if (f == NULL) {
2930 cmd_alert(L'\0');
2931 } else {
2932 wint_t c;
2933
2934 wb_clear(&le_main_buffer);
2935 while ((c = fgetwc(f)) != WEOF)
2936 wb_wccat(&le_main_buffer, (wchar_t) c);
2937 fclose(f);
2938
2939 /* remove trailing newline */
2940 while (le_main_buffer.length > 0 &&
2941 le_main_buffer.contents[le_main_buffer.length - 1] == L'\n')
2942 wb_remove(&le_main_buffer, le_main_buffer.length - 1, 1);
2943
2944 le_main_index = le_main_buffer.length;
2945
2946 le_editstate = LE_EDITSTATE_DONE;
2947 end:
2948 reset_state();
2949 }
2950
2951 laststatus = savelaststatus;
2952 unlink(tempfile);
2953 free(tempfile);
2954 if (shopt_notify || shopt_notifyle)
2955 print_job_status_all();
2956 le_resume_readline();
2957 } else { // child process
2958 fwprintf(f, L"%ls\n", le_main_buffer.contents);
2959 fclose(f);
2960
2961 wchar_t *command = malloc_wprintf(L"vi %s", tempfile);
2962 free(tempfile);
2963 exec_wcs(command, gt("lineedit"), true);
2964 #ifndef NDEBUG
2965 free(command);
2966 #endif
2967 assert(false);
2968 }
2969 }
2970
2971 /* Performs command line completion and
2972 * * if the count is not set, list all the candidates without changing the
2973 * main buffer.
2974 * * if the count is set, complete the `count'th candidate and set the mode
2975 * to vi-insert. */
cmd_vi_complete_list(wchar_t c)2976 void cmd_vi_complete_list(wchar_t c __attribute__((unused)))
2977 {
2978 ALERT_AND_RETURN_IF_PENDING;
2979 maybe_save_undo_history();
2980 clear_prediction();
2981 le_complete_cleanup();
2982 /* leave `next_reset_completion' to be true because the results of this
2983 * command cannot be used by succeeding completion commands. */
2984 // next_reset_completion = false;
2985
2986 size_t oldindex = le_main_index;
2987 if (le_main_index < le_main_buffer.length)
2988 le_main_index++;
2989
2990 if (le_complete_fix_candidate(get_count(0))) {
2991 cmd_setmode_viinsert(L'\0');
2992 } else {
2993 le_main_index = oldindex;
2994 reset_state();
2995 }
2996 }
2997
2998 /* Performs command line completion and replaces the current word with all of
2999 * the generated candidates.
3000 * The mode is changed to vi-insert. */
cmd_vi_complete_all(wchar_t c)3001 void cmd_vi_complete_all(wchar_t c __attribute__((unused)))
3002 {
3003 ALERT_AND_RETURN_IF_PENDING;
3004 clear_prediction();
3005 check_reset_completion();
3006
3007 if (le_main_index < le_main_buffer.length)
3008 le_main_index++;
3009 le_complete(lecr_substitute_all_candidates);
3010
3011 cmd_setmode_viinsert(L'\0');
3012 }
3013
3014 /* Performs command line completion and replaces the current word with the
3015 * longest common prefix of the candidates.
3016 * The mode is changed to vi-insert. */
cmd_vi_complete_max(wchar_t c)3017 void cmd_vi_complete_max(wchar_t c __attribute__((unused)))
3018 {
3019 ALERT_AND_RETURN_IF_PENDING;
3020 clear_prediction();
3021 check_reset_completion();
3022
3023 if (le_main_index < le_main_buffer.length)
3024 le_main_index++;
3025 le_complete(lecr_longest_common_prefix);
3026
3027 cmd_setmode_viinsert(L'\0');
3028 }
3029
3030 /* Starts vi-like command history search in the forward direction. */
cmd_vi_search_forward(wchar_t c)3031 void cmd_vi_search_forward(wchar_t c __attribute__((unused)))
3032 {
3033 ALERT_AND_RETURN_IF_PENDING;
3034 set_search_mode(LE_MODE_VI_SEARCH, FORWARD);
3035 }
3036
3037 /* Starts vi-like command history search in the backward direction. */
cmd_vi_search_backward(wchar_t c)3038 void cmd_vi_search_backward(wchar_t c __attribute__((unused)))
3039 {
3040 ALERT_AND_RETURN_IF_PENDING;
3041 set_search_mode(LE_MODE_VI_SEARCH, BACKWARD);
3042 }
3043
3044
3045 /********** Emacs-Mode Specific Commands **********/
3046
3047 /* Moves the character before the cursor to the right by `count' characters. */
cmd_emacs_transpose_chars(wchar_t c)3048 void cmd_emacs_transpose_chars(wchar_t c __attribute__((unused)))
3049 {
3050 //ALERT_AND_RETURN_IF_PENDING;
3051 if (state.pending_command_motion != MEC_MOVE || le_main_index == 0)
3052 goto error;
3053 maybe_save_undo_history();
3054 if (state.count.sign == 0
3055 && le_main_index == le_main_buffer.length && le_main_index >= 2)
3056 le_main_index--;
3057
3058 int count = get_count(1);
3059 size_t index;
3060
3061 if (count >= 0) {
3062 #if COUNT_ABS_MAX > SIZE_MAX
3063 if (count <= (int) (le_main_buffer.length - le_main_index))
3064 #else
3065 if ((size_t) count <= le_main_buffer.length - le_main_index)
3066 #endif
3067 index = le_main_index + (size_t) count;
3068 else {
3069 le_main_index = le_main_buffer.length;
3070 goto error;
3071 }
3072 } else {
3073 #if COUNT_ABS_MAX > SIZE_MAX
3074 if (-count < (int) le_main_index)
3075 #else
3076 if ((size_t) -count < le_main_index)
3077 #endif
3078 index = le_main_index + (size_t) count;
3079 else {
3080 le_main_index = 0;
3081 goto error;
3082 }
3083 }
3084
3085 size_t old_index = le_main_index;
3086
3087 assert(le_main_index > 0);
3088 assert(0 < index && index <= le_main_buffer.length);
3089 c = le_main_buffer.contents[old_index - 1];
3090 wb_remove(&le_main_buffer, old_index - 1, 1);
3091 wb_ninsert(&le_main_buffer, index - 1, &c, 1);
3092 le_main_index = index;
3093
3094 if (le_main_length < old_index)
3095 le_main_length = old_index;
3096 if (le_main_length < index)
3097 le_main_length = index;
3098
3099 reset_state();
3100 return;
3101
3102 error:
3103 cmd_alert(L'\0');
3104 }
3105
3106 /* Interchanges the word before the cursor and the `count' words after the
3107 * cursor. */
cmd_emacs_transpose_words(wchar_t c)3108 void cmd_emacs_transpose_words(wchar_t c __attribute__((unused)))
3109 {
3110 if (state.pending_command_motion != MEC_MOVE || le_main_index == 0)
3111 goto error;
3112 maybe_save_undo_history();
3113
3114 int count = get_count(1);
3115 size_t w1start, w1end, w2start, w2end, new_index;
3116 xwcsbuf_T buf;
3117
3118 if (count == 0)
3119 goto end;
3120
3121 w1start = previous_emacsword_index(le_main_buffer.contents, le_main_index);
3122 w1end = next_emacsword_index(le_main_buffer.contents, w1start);
3123 if (count >= 0) {
3124 w2end = next_emacsword_index(le_main_buffer.contents, w1end);
3125 w2start = previous_emacsword_index(le_main_buffer.contents, w2end);
3126 while (--count > 0) {
3127 if (w2end == le_main_buffer.length)
3128 goto error;
3129 w2end = next_emacsword_index(le_main_buffer.contents, w2end);
3130 }
3131 new_index = w2end;
3132 } else {
3133 w2start = w1start, w2end = w1end;
3134 w1start = previous_emacsword_index(le_main_buffer.contents, w2start);
3135 w1end = next_emacsword_index(le_main_buffer.contents, w1start);
3136 while (++count < 0) {
3137 if (w1start == 0)
3138 goto error;
3139 w1start = previous_emacsword_index(
3140 le_main_buffer.contents, w1start);
3141 }
3142 new_index = w1start + (w2end - w2start);
3143 }
3144 if (w1end >= w2start)
3145 goto error;
3146 wb_initwithmax(&buf, w2end - w1start);
3147 wb_ncat_force(&buf, &le_main_buffer.contents[w2start], w2end - w2start);
3148 wb_ncat_force(&buf, &le_main_buffer.contents[w1end], w2start - w1end);
3149 wb_ncat_force(&buf, &le_main_buffer.contents[w1start], w1end - w1start);
3150 assert(buf.length == w2end - w1start);
3151 wb_replace_force(&le_main_buffer, w1start, buf.length,
3152 buf.contents, buf.length);
3153 wb_destroy(&buf);
3154 le_main_index = new_index;
3155 if (le_main_length < w2end)
3156 le_main_length = w2end;
3157 end:
3158 reset_state();
3159 return;
3160
3161 error:
3162 cmd_alert(L'\0');
3163 }
3164
3165 /* Converts the word after the cursor to lower case.
3166 * If the count is set, `count' words are converted.
3167 * The cursor is left after the last converted word. */
cmd_emacs_downcase_word(wchar_t c)3168 void cmd_emacs_downcase_word(wchar_t c __attribute__((unused)))
3169 {
3170 exec_motion_expect_command(
3171 MEC_LOWERCASE | MEC_TOEND, cmd_forward_emacsword);
3172 }
3173
3174 /* Converts `count' words after the cursor to upper case.
3175 * If the count is set, `count' words are converted.
3176 * The cursor is left after the last converted word. */
cmd_emacs_upcase_word(wchar_t c)3177 void cmd_emacs_upcase_word(wchar_t c __attribute__((unused)))
3178 {
3179 exec_motion_expect_command(
3180 MEC_UPPERCASE | MEC_TOEND, cmd_forward_emacsword);
3181 }
3182
3183 /* Capitalizes the word after the cursor.
3184 * If the count is set, `count' words are capitalized.
3185 * The cursor is left after the last capitalized word. */
cmd_emacs_capitalize_word(wchar_t c)3186 void cmd_emacs_capitalize_word(wchar_t c __attribute__((unused)))
3187 {
3188 ALERT_AND_RETURN_IF_PENDING;
3189 maybe_save_undo_history();
3190
3191 int count = get_count(1);
3192
3193 if (count > 0) {
3194 wchar_t *s = &le_main_buffer.contents[le_main_index];
3195 do {
3196 while (*s != L'\0' && !iswalnum(*s))
3197 s++;
3198 *s = towupper(*s);
3199 s++;
3200 while (*s != L'\0' && iswalnum(*s))
3201 s++;
3202 } while (*s != L'\0' && --count > 0);
3203 le_main_index = s - le_main_buffer.contents;
3204 } else if (count < 0) {
3205 size_t index = le_main_index;
3206 do {
3207 index = previous_emacsword_index(le_main_buffer.contents, index);
3208 le_main_buffer.contents[index] =
3209 towupper(le_main_buffer.contents[index]);
3210 } while (index > 0 && ++count < 0);
3211 }
3212
3213 if (le_main_length < le_main_index)
3214 le_main_length = le_main_index;
3215
3216 reset_state();
3217 }
3218
3219 /* Deletes blank characters around the cursor.
3220 * If the count is set, only blanks before the cursor are deleted. */
cmd_emacs_delete_horizontal_space(wchar_t c)3221 void cmd_emacs_delete_horizontal_space(wchar_t c __attribute__((unused)))
3222 {
3223 replace_horizontal_space(state.count.sign == 0, L"");
3224 }
3225
3226 /* Replaces blank characters around the cursor with a space.
3227 * The cursor is left after the space.
3228 * A space is inserted even if there are no blanks around the cursor.
3229 * If the count is specified, blanks are replaced with `count' spaces. */
cmd_emacs_just_one_space(wchar_t c)3230 void cmd_emacs_just_one_space(wchar_t c __attribute__((unused)))
3231 {
3232 int count = get_count(1);
3233 if (count < 0)
3234 count = 0;
3235 else if (count > 1000)
3236 count = 1000;
3237
3238 wchar_t s[count + 1];
3239 wmemset(s, L' ', count);
3240 s[count] = L'\0';
3241
3242 replace_horizontal_space(true, s);
3243 }
3244
3245 /* Replaces blank characters around the cursor with the specified string.
3246 * If `deleteafter' is true, blanks after the cursor are replaced as well as
3247 * blanks before the cursor. If `deleteafter' is false, only blanks before the
3248 * cursor are replaced.
3249 * The cursor is left after the replacement. */
replace_horizontal_space(bool deleteafter,const wchar_t * s)3250 void replace_horizontal_space(bool deleteafter, const wchar_t *s)
3251 {
3252 ALERT_AND_RETURN_IF_PENDING;
3253 maybe_save_undo_history();
3254
3255 size_t start_index = le_main_index;
3256 while (start_index > 0
3257 && iswblank(le_main_buffer.contents[start_index - 1]))
3258 start_index--;
3259
3260 size_t end_index = le_main_index;
3261 if (deleteafter)
3262 while (end_index < le_main_buffer.length
3263 && iswblank(le_main_buffer.contents[end_index]))
3264 end_index++;
3265
3266 if (le_main_length < end_index)
3267 le_main_length = end_index;
3268
3269 size_t slen = wcslen(s);
3270 wb_replace_force(&le_main_buffer, start_index, end_index - start_index,
3271 s, slen);
3272
3273 le_main_index = start_index + slen;
3274 le_main_length += le_main_index - end_index;
3275
3276 reset_state();
3277 }
3278
3279 /* Starts emacs-like command history search in the forward direction. */
cmd_emacs_search_forward(wchar_t c)3280 void cmd_emacs_search_forward(wchar_t c __attribute__((unused)))
3281 {
3282 ALERT_AND_RETURN_IF_PENDING;
3283 set_search_mode(LE_MODE_EMACS_SEARCH, FORWARD);
3284 }
3285
3286 /* Starts emacs-like command history search in the backward direction. */
cmd_emacs_search_backward(wchar_t c)3287 void cmd_emacs_search_backward(wchar_t c __attribute__((unused)))
3288 {
3289 ALERT_AND_RETURN_IF_PENDING;
3290 set_search_mode(LE_MODE_EMACS_SEARCH, BACKWARD);
3291 }
3292
3293
3294 /********** History-Related Commands **********/
3295
3296 /* Goes to the oldest history entry.
3297 * If the count is specified, goes to the history entry whose number is count.
3298 * If the specified entry is not found, the terminal is alerted.
3299 * The cursor position is not changed. */
cmd_oldest_history(wchar_t c)3300 void cmd_oldest_history(wchar_t c __attribute__((unused)))
3301 {
3302 go_to_history_absolute(histlist.Oldest, SEARCH_PREFIX);
3303 }
3304
3305 /* Goes to the newest history entry.
3306 * If the count is specified, goes to the history entry whose number is count.
3307 * If the specified entry is not found, the terminal is alerted.
3308 * The cursor position is not changed. */
cmd_newest_history(wchar_t c)3309 void cmd_newest_history(wchar_t c __attribute__((unused)))
3310 {
3311 go_to_history_absolute(histlist.Newest, SEARCH_PREFIX);
3312 }
3313
3314 /* Goes to the newest history entry.
3315 * If the count is specified, goes to the history entry whose number is count.
3316 * If the specified entry is not found, the terminal is alerted.
3317 * The cursor position is not changed. */
cmd_return_history(wchar_t c)3318 void cmd_return_history(wchar_t c __attribute__((unused)))
3319 {
3320 go_to_history_absolute(Histlist, SEARCH_PREFIX);
3321 }
3322
3323 /* Goes to the oldest history entry.
3324 * If the count is specified, goes to the history entry whose number is count.
3325 * If the specified entry is not found, the terminal is alerted.
3326 * The cursor is put at the beginning of line. */
cmd_oldest_history_bol(wchar_t c)3327 void cmd_oldest_history_bol(wchar_t c __attribute__((unused)))
3328 {
3329 go_to_history_absolute(histlist.Oldest, SEARCH_VI);
3330 }
3331
3332 /* Goes to the newest history entry.
3333 * If the count is specified, goes to the history entry whose number is count.
3334 * If the specified entry is not found, the terminal is alerted.
3335 * The cursor is put at the beginning of line. */
cmd_newest_history_bol(wchar_t c)3336 void cmd_newest_history_bol(wchar_t c __attribute__((unused)))
3337 {
3338 go_to_history_absolute(histlist.Newest, SEARCH_VI);
3339 }
3340
3341 /* Goes to the newest history entry.
3342 * If the count is specified, goes to the history entry whose number is count.
3343 * If the specified entry is not found, the terminal is alerted.
3344 * The cursor is put at the beginning of line. */
cmd_return_history_bol(wchar_t c)3345 void cmd_return_history_bol(wchar_t c __attribute__((unused)))
3346 {
3347 go_to_history_absolute(Histlist, SEARCH_VI);
3348 }
3349
3350 /* Goes to the oldest history entry.
3351 * If the count is specified, goes to the history entry whose number is count.
3352 * If the specified entry is not found, the terminal is alerted.
3353 * The cursor is put at the end of line. */
cmd_oldest_history_eol(wchar_t c)3354 void cmd_oldest_history_eol(wchar_t c __attribute__((unused)))
3355 {
3356 go_to_history_absolute(histlist.Oldest, SEARCH_EMACS);
3357 }
3358
3359 /* Goes to the newest history entry.
3360 * If the count is specified, goes to the history entry whose number is count.
3361 * If the specified entry is not found, the terminal is alerted.
3362 * The cursor is put at the end of line. */
cmd_newest_history_eol(wchar_t c)3363 void cmd_newest_history_eol(wchar_t c __attribute__((unused)))
3364 {
3365 go_to_history_absolute(histlist.Newest, SEARCH_EMACS);
3366 }
3367
3368 /* Goes to the newest history entry.
3369 * If the count is specified, goes to the history entry whose number is count.
3370 * If the specified entry is not found, the terminal is alerted.
3371 * The cursor is put at the end of line. */
cmd_return_history_eol(wchar_t c)3372 void cmd_return_history_eol(wchar_t c __attribute__((unused)))
3373 {
3374 go_to_history_absolute(Histlist, SEARCH_EMACS);
3375 }
3376
3377 /* Goes to the specified history entry.
3378 * If the count is specified, goes to the history entry whose number is count.
3379 * If the specified entry is not found, the terminal is alerted.
3380 * See `go_to_history' for the meaning of `curpos'. */
go_to_history_absolute(const histlink_T * l,enum le_search_type_T curpos)3381 void go_to_history_absolute(const histlink_T *l, enum le_search_type_T curpos)
3382 {
3383 ALERT_AND_RETURN_IF_PENDING;
3384
3385 if (state.count.sign == 0) {
3386 if (histlist.count == 0)
3387 goto alert;
3388 } else {
3389 int num = get_count(0);
3390 if (num <= 0)
3391 goto alert;
3392 l = get_history_entry((unsigned) num);
3393 if (l == Histlist)
3394 goto alert;
3395 }
3396 go_to_history(l, curpos);
3397 reset_state();
3398 return;
3399
3400 alert:
3401 cmd_alert(L'\0');
3402 return;
3403 }
3404
3405 /* Goes to the `count'th next history entry.
3406 * The cursor position is not changed. */
cmd_next_history(wchar_t c)3407 void cmd_next_history(wchar_t c __attribute__((unused)))
3408 {
3409 ALERT_AND_RETURN_IF_PENDING;
3410 go_to_history_relative(get_count(1), SEARCH_PREFIX);
3411 }
3412
3413 /* Goes to the `count'th previous history entry.
3414 * The cursor position is not changed. */
cmd_prev_history(wchar_t c)3415 void cmd_prev_history(wchar_t c __attribute__((unused)))
3416 {
3417 ALERT_AND_RETURN_IF_PENDING;
3418 go_to_history_relative(-get_count(1), SEARCH_PREFIX);
3419 }
3420
3421 /* Goes to the `count'th next history entry.
3422 * The cursor is put at the beginning of line. */
cmd_next_history_bol(wchar_t c)3423 void cmd_next_history_bol(wchar_t c __attribute__((unused)))
3424 {
3425 ALERT_AND_RETURN_IF_PENDING;
3426 go_to_history_relative(get_count(1), SEARCH_VI);
3427 }
3428
3429 /* Goes to the `count'th previous history entry.
3430 * The cursor is put at the beginning of line. */
cmd_prev_history_bol(wchar_t c)3431 void cmd_prev_history_bol(wchar_t c __attribute__((unused)))
3432 {
3433 ALERT_AND_RETURN_IF_PENDING;
3434 go_to_history_relative(-get_count(1), SEARCH_VI);
3435 }
3436
3437 /* Goes to the `count'th next history entry.
3438 * The cursor is put at the end of line. */
cmd_next_history_eol(wchar_t c)3439 void cmd_next_history_eol(wchar_t c __attribute__((unused)))
3440 {
3441 ALERT_AND_RETURN_IF_PENDING;
3442 go_to_history_relative(get_count(1), SEARCH_EMACS);
3443 }
3444
3445 /* Goes to the `count'th previous history entry.
3446 * The cursor is put at the end of line. */
cmd_prev_history_eol(wchar_t c)3447 void cmd_prev_history_eol(wchar_t c __attribute__((unused)))
3448 {
3449 ALERT_AND_RETURN_IF_PENDING;
3450 go_to_history_relative(-get_count(1), SEARCH_EMACS);
3451 }
3452
3453 /* Goes to the `offset'th next history entry.
3454 * See `go_to_history' for the meaning of `curpos'. */
go_to_history_relative(int offset,enum le_search_type_T curpos)3455 void go_to_history_relative(int offset, enum le_search_type_T curpos)
3456 {
3457 const histlink_T *l = main_history_entry;
3458 if (offset > 0) {
3459 do {
3460 if (l == Histlist)
3461 goto alert;
3462 l = l->next;
3463 } while (--offset > 0);
3464 } else if (offset < 0) {
3465 do {
3466 l = l->prev;
3467 if (l == Histlist)
3468 goto alert;
3469 } while (++offset < 0);
3470 }
3471 go_to_history(l, curpos);
3472 reset_state();
3473 return;
3474
3475 alert:
3476 cmd_alert(L'\0');
3477 return;
3478 }
3479
3480 /* Sets the value of the specified history entry to the main buffer.
3481 * The value of `curpos' specifies where the cursor is left:
3482 * SEARCH_PREFIX: the current position (unless it exceeds the buffer length)
3483 * SEARCH_VI: the beginning of the buffer
3484 * SEARCH_EMACS: the end of the buffer */
go_to_history(const histlink_T * l,enum le_search_type_T curpos)3485 void go_to_history(const histlink_T *l, enum le_search_type_T curpos)
3486 {
3487 maybe_save_undo_history();
3488 clear_prediction();
3489
3490 free(main_history_value);
3491 wb_clear(&le_main_buffer);
3492 if (l == undo_history_entry && undo_index < undo_history.length) {
3493 struct undo_history *h = undo_history.contents[undo_index];
3494 wb_cat(&le_main_buffer, h->contents);
3495 assert(h->index <= le_main_buffer.length);
3496 le_main_index = h->index;
3497 } else {
3498 if (l != Histlist)
3499 wb_mbscat(&le_main_buffer, ashistentry(l)->value);
3500 switch (curpos) {
3501 case SEARCH_PREFIX:
3502 if (le_main_index > le_main_buffer.length)
3503 le_main_index = le_main_buffer.length;
3504 break;
3505 case SEARCH_VI:
3506 le_main_index = 0;
3507 break;
3508 case SEARCH_EMACS:
3509 le_main_index = le_main_buffer.length;
3510 break;
3511 }
3512 }
3513 main_history_entry = l;
3514 main_history_value = xwcsdup(le_main_buffer.contents);
3515 undo_save_index = le_main_index;
3516 }
3517
3518 /***** History Search Commands *****/
3519
3520 /* Appends the argument character to the search buffer. */
cmd_srch_self_insert(wchar_t c)3521 void cmd_srch_self_insert(wchar_t c)
3522 {
3523 if (le_search_buffer.contents == NULL || c == L'\0') {
3524 cmd_alert(L'\0');
3525 return;
3526 }
3527
3528 wb_wccat(&le_search_buffer, c);
3529 update_search();
3530 }
3531
3532 /* Removes the last character from the search buffer.
3533 * If there are no characters in the buffer, calls `cmd_srch_abort_search' (for
3534 * vi-like search) or `cmd_alert' (for emacs-like search). */
cmd_srch_backward_delete_char(wchar_t c)3535 void cmd_srch_backward_delete_char(wchar_t c __attribute__((unused)))
3536 {
3537 if (le_search_buffer.contents == NULL) {
3538 cmd_alert(L'\0');
3539 return;
3540 }
3541
3542 if (le_search_buffer.length == 0) {
3543 switch (le_search_type) {
3544 case SEARCH_VI:
3545 cmd_srch_abort_search(L'\0');
3546 return;
3547 case SEARCH_EMACS:
3548 cmd_alert(L'\0');
3549 return;
3550 case SEARCH_PREFIX:
3551 assert(false);
3552 }
3553 }
3554
3555 wb_remove(&le_search_buffer, le_search_buffer.length - 1, 1);
3556 update_search();
3557 }
3558
3559 /* Removes all characters from the search buffer. */
cmd_srch_backward_delete_line(wchar_t c)3560 void cmd_srch_backward_delete_line(wchar_t c __attribute__((unused)))
3561 {
3562 if (le_search_buffer.contents == NULL) {
3563 cmd_alert(L'\0');
3564 return;
3565 }
3566
3567 wb_clear(&le_search_buffer);
3568 update_search();
3569 }
3570
3571 /* Settles the current search result and continues the search with the current
3572 * pattern in the forward direction. This is like `cmd_search_again_forward'
3573 * but this command can be used during the search. */
cmd_srch_continue_forward(wchar_t c)3574 void cmd_srch_continue_forward(wchar_t c __attribute__((unused)))
3575 {
3576 if (le_search_buffer.contents == NULL) {
3577 cmd_alert(L'\0');
3578 return;
3579 }
3580
3581 le_search_direction = FORWARD;
3582 if (le_search_result != Histlist)
3583 go_to_history(le_search_result, le_search_type);
3584 update_search();
3585 }
3586
3587 /* Settles the current search result and continues the search with the current
3588 * pattern in the backward direction. This is like `cmd_search_again_backward'
3589 * but this command can be used during the search. */
cmd_srch_continue_backward(wchar_t c)3590 void cmd_srch_continue_backward(wchar_t c __attribute__((unused)))
3591 {
3592 if (le_search_buffer.contents == NULL) {
3593 cmd_alert(L'\0');
3594 return;
3595 }
3596
3597 le_search_direction = BACKWARD;
3598 if (le_search_result != Histlist)
3599 go_to_history(le_search_result, le_search_type);
3600 update_search();
3601 }
3602
3603 /* Finishes the history search and accepts the current result candidate.
3604 * If no search is being performed, does nothing. */
cmd_srch_accept_search(wchar_t c)3605 void cmd_srch_accept_search(wchar_t c __attribute__((unused)))
3606 {
3607 if (le_search_buffer.contents == NULL)
3608 return;
3609
3610 last_search.direction = le_search_direction;
3611 last_search.type = le_search_type;
3612 if (need_update_last_search_value()) {
3613 free(last_search.value);
3614 last_search.value = wb_towcs(&le_search_buffer);
3615 } else {
3616 wb_destroy(&le_search_buffer);
3617 }
3618 le_search_buffer.contents = NULL;
3619 le_set_mode(savemode);
3620 if (le_search_result == Histlist) {
3621 cmd_alert(L'\0');
3622 } else {
3623 go_to_history(le_search_result, le_search_type);
3624 }
3625 reset_state();
3626 }
3627
3628 /* Checks if we should update `last_search.value' to the current value of
3629 * `le_search_buffer'. */
need_update_last_search_value(void)3630 bool need_update_last_search_value(void)
3631 {
3632 switch (le_search_type) {
3633 case SEARCH_PREFIX:
3634 break;
3635 case SEARCH_VI:
3636 if (le_search_buffer.contents[0] == L'\0')
3637 return false;
3638 if (le_search_buffer.contents[0] == L'^'
3639 && le_search_buffer.contents[1] == L'\0')
3640 return false;
3641 return true;
3642 case SEARCH_EMACS:
3643 return le_search_buffer.contents[0] != L'\0';
3644 }
3645 assert(false);
3646 }
3647
3648 /* Aborts the history search.
3649 * If no search is being performed, does nothing. */
cmd_srch_abort_search(wchar_t c)3650 void cmd_srch_abort_search(wchar_t c __attribute__((unused)))
3651 {
3652 if (le_search_buffer.contents == NULL)
3653 return;
3654
3655 wb_destroy(&le_search_buffer);
3656 le_search_buffer.contents = NULL;
3657 le_set_mode(savemode);
3658 reset_state();
3659 }
3660
3661 /* Re-calculates the search result candidate. */
update_search(void)3662 void update_search(void)
3663 {
3664 const wchar_t *pattern = le_search_buffer.contents;
3665 if (pattern[0] == L'\0') {
3666 switch (le_search_type) {
3667 case SEARCH_PREFIX:
3668 break;
3669 case SEARCH_VI:
3670 pattern = last_search.value;
3671 if (pattern == NULL) {
3672 le_search_result = Histlist;
3673 goto done;
3674 }
3675 break;
3676 case SEARCH_EMACS:
3677 le_search_result = Histlist;
3678 goto done;
3679 }
3680 }
3681
3682 perform_search(pattern, le_search_direction, le_search_type);
3683 done:
3684 reset_state();
3685 }
3686
3687 /* Performs history search with the given parameters and updates the result
3688 * candidate. */
perform_search(const wchar_t * pattern,enum le_search_direction_T dir,enum le_search_type_T type)3689 void perform_search(const wchar_t *pattern,
3690 enum le_search_direction_T dir, enum le_search_type_T type)
3691 {
3692 const histlink_T *l = main_history_entry;
3693 xfnmatch_T *xfnm;
3694
3695 if (dir == FORWARD && l == Histlist)
3696 goto done;
3697
3698 switch (type) {
3699 case SEARCH_PREFIX: {
3700 wchar_t *p = escape(pattern, NULL);
3701 xfnm = xfnm_compile(p, XFNM_HEADONLY);
3702 free(p);
3703 break;
3704 }
3705 case SEARCH_VI: {
3706 xfnmflags_T flags = 0;
3707 if (pattern[0] == L'^') {
3708 flags |= XFNM_HEADONLY;
3709 pattern++;
3710 if (pattern[0] == L'\0') {
3711 l = Histlist;
3712 goto done;
3713 }
3714 }
3715 xfnm = xfnm_compile(pattern, flags);
3716 break;
3717 }
3718 case SEARCH_EMACS: {
3719 wchar_t *p = escape(pattern, NULL);
3720 xfnm = xfnm_compile(p, 0);
3721 free(p);
3722 break;
3723 }
3724 default:
3725 assert(false);
3726 }
3727 if (xfnm == NULL) {
3728 l = Histlist;
3729 goto done;
3730 }
3731
3732 for (;;) {
3733 switch (dir) {
3734 case FORWARD: l = l->next; break;
3735 case BACKWARD: l = l->prev; break;
3736 }
3737 if (l == Histlist)
3738 break;
3739 if (xfnm_match(xfnm, ashistentry(l)->value) == 0)
3740 break;
3741 }
3742 xfnm_free(xfnm);
3743 done:
3744 le_search_result = l;
3745 }
3746
3747 /* Redoes the last search. */
cmd_search_again(wchar_t c)3748 void cmd_search_again(wchar_t c __attribute__((unused)))
3749 {
3750 search_again(last_search.direction);
3751 }
3752
3753 /* Redoes the last search in the reverse direction. */
cmd_search_again_rev(wchar_t c)3754 void cmd_search_again_rev(wchar_t c __attribute__((unused)))
3755 {
3756 switch (last_search.direction) {
3757 case FORWARD: search_again(BACKWARD); break;
3758 case BACKWARD: search_again(FORWARD); break;
3759 }
3760 }
3761
3762 /* Redoes the last search in the forward direction. */
cmd_search_again_forward(wchar_t c)3763 void cmd_search_again_forward(wchar_t c __attribute__((unused)))
3764 {
3765 search_again(FORWARD);
3766 }
3767
3768 /* Redoes the last search in the backward direction. */
cmd_search_again_backward(wchar_t c)3769 void cmd_search_again_backward(wchar_t c __attribute__((unused)))
3770 {
3771 search_again(BACKWARD);
3772 }
3773
3774 /* Performs command search for the last search pattern in the specified
3775 * direction. If the count is set, re-search `count' times. If the count is
3776 * negative, search in the opposite direction. */
search_again(enum le_search_direction_T dir)3777 void search_again(enum le_search_direction_T dir)
3778 {
3779 ALERT_AND_RETURN_IF_PENDING;
3780
3781 if (last_search.value == NULL) {
3782 cmd_alert(L'\0');
3783 return;
3784 }
3785
3786 int count = get_count(1);
3787 if (count < 0) {
3788 count = -count;
3789 switch (dir) {
3790 case FORWARD: dir = BACKWARD; break;
3791 case BACKWARD: dir = FORWARD; break;
3792 }
3793 }
3794
3795 while (--count >= 0) {
3796 perform_search(last_search.value, dir, last_search.type);
3797 if (le_search_result == Histlist) {
3798 cmd_alert(L'\0');
3799 break;
3800 } else {
3801 go_to_history(le_search_result, last_search.type);
3802 }
3803 }
3804 reset_state();
3805 }
3806
3807 /* Searches the history in the forward direction for an entry that has a common
3808 * prefix with the current buffer contents. The "prefix" is the first
3809 * `le_main_index' characters of the buffer. */
cmd_beginning_search_forward(wchar_t c)3810 void cmd_beginning_search_forward(wchar_t c __attribute__((unused)))
3811 {
3812 beginning_search(FORWARD);
3813 }
3814
3815 /* Searches the history in the backward direction for an entry that has a common
3816 * prefix with the current buffer contents. The "prefix" is the first
3817 * `le_main_index' characters of the buffer. */
cmd_beginning_search_backward(wchar_t c)3818 void cmd_beginning_search_backward(wchar_t c __attribute__((unused)))
3819 {
3820 beginning_search(BACKWARD);
3821 }
3822
3823 /* Searches the history in the specified direction for an entry that has a
3824 * common prefix with the current buffer contents. The "prefix" is the first
3825 * `le_main_index' characters of the buffer. */
beginning_search(enum le_search_direction_T dir)3826 void beginning_search(enum le_search_direction_T dir)
3827 {
3828 ALERT_AND_RETURN_IF_PENDING;
3829
3830 int count = get_count(1);
3831 if (count < 0) {
3832 count = -count;
3833 switch (dir) {
3834 case FORWARD: dir = BACKWARD; break;
3835 case BACKWARD: dir = FORWARD; break;
3836 }
3837 }
3838
3839 wchar_t *prefix = xwcsndup(le_main_buffer.contents, le_main_index);
3840 while (--count >= 0) {
3841 perform_search(prefix, dir, SEARCH_PREFIX);
3842 if (le_search_result == Histlist) {
3843 if (dir == FORWARD && beginning_search_check_go_to_history(prefix))
3844 go_to_history(le_search_result, SEARCH_PREFIX);
3845 else
3846 cmd_alert(L'\0');
3847 break;
3848 } else {
3849 go_to_history(le_search_result, SEARCH_PREFIX);
3850 }
3851 }
3852 free(prefix);
3853 reset_state();
3854 }
3855
beginning_search_check_go_to_history(const wchar_t * prefix)3856 bool beginning_search_check_go_to_history(const wchar_t *prefix)
3857 {
3858 if (le_search_result == undo_history_entry
3859 && undo_index < undo_history.length) {
3860 const struct undo_history *h = undo_history.contents[undo_index];
3861 return matchwcsprefix(h->contents, prefix) != NULL;
3862 } else {
3863 return false;
3864 }
3865 }
3866
3867
3868 /* vim: set ts=8 sts=4 sw=4 noet tw=80: */
3869