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