-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathediting.c
More file actions
3907 lines (3421 loc) · 118 KB
/
Copy pathediting.c
File metadata and controls
3907 lines (3421 loc) · 118 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Yash: yet another shell */
/* editing.c: main editing module */
/* (C) 2007-2026 magicant */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "../common.h"
#include "editing.h"
#include <assert.h>
#include <errno.h>
#if HAVE_GETTEXT
# include <libintl.h>
#endif
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wchar.h>
#include <wctype.h>
#include "../alias.h"
#include "../exec.h"
#include "../expand.h"
#include "../history.h"
#include "../job.h"
#include "../option.h"
#include "../path.h"
#include "../plist.h"
#include "../redir.h"
#include "../strbuf.h"
#include "../util.h"
#include "../xfnmatch.h"
#include "../yash.h"
#include "complete.h"
#include "display.h"
#include "keymap.h"
#include "lineedit.h"
#include "terminfo.h"
#include "trie.h"
/* The type of pairs of a command and an argument. */
struct le_command_T {
le_command_func_T *func;
wchar_t arg;
};
/* The main buffer where the command line is edited.
* The contents of the buffer is divided into two parts: The first part is the
* main command line text that is input and edited by the user. The second is
* automatically appended after the first as a result of the prediction
* feature. As the user edits the first part, the prediction feature updates
* the second. When the user moves the cursor to somewhere in the second part,
* the text up to the cursor then becomes the first. */
xwcsbuf_T le_main_buffer;
/* The position that divides the main buffer into two parts as described just
* above. If `le_main_length > le_main_buffer.length', the second part is
* assumed empty. */
size_t le_main_length;
/* The position of the cursor on the command line.
* le_main_index <= le_main_buffer.length */
size_t le_main_index;
/* The history entry that is being edited in the main buffer now.
* When we're editing no history entry, `main_history_entry' is `Histlist'. */
static const histlink_T *main_history_entry;
/* The original value of `main_history_entry', converted into a wide string. */
static wchar_t *main_history_value;
/* The direction of currently performed command history search. */
enum le_search_direction_T le_search_direction;
/* The type of currently performed command history search. */
enum le_search_type_T le_search_type;
/* Supplementary buffer used in command history search.
* When search is not being performed, `le_search_buffer.contents' is NULL. */
xwcsbuf_T le_search_buffer;
/* The search result for the current value of `le_search_buffer'.
* If there is no match, `le_search_result' is `Histlist'. */
const histlink_T *le_search_result;
/* The search string and the direction of the last search. */
static struct {
enum le_search_direction_T direction;
enum le_search_type_T type;
wchar_t *value;
} last_search;
/* The last executed command and the currently executing command. */
static struct le_command_T last_command, current_command;
/* The type of motion expecting commands. */
enum motion_expect_command_T {
MEC_UPPERCASE = 1 << 0, /* convert the text to upper case */
MEC_LOWERCASE = 1 << 1, /* convert the text to lower case */
MEC_SWITCHCASE = MEC_UPPERCASE | MEC_LOWERCASE, /* switch case of text */
MEC_CASEMASK = MEC_SWITCHCASE,
MEC_TOSTART = 1 << 2, /* move cursor to the beginning of the region */
MEC_TOEND = 1 << 3, /* move cursor to the end of the region */
MEC_MOVE = MEC_TOSTART | MEC_TOEND, /* move cursor to motion end */
MEC_CURSORMASK = MEC_MOVE,
/* If none of MEC_TOSTART, MEC_TOEND, and MEC_MOVE is specified, the cursor
* is not moved unless MEC_DELETE is specified. */
MEC_COPY = 1 << 4, /* copy the text to the kill ring */
MEC_DELETE = 1 << 5, /* delete the text */
MEC_INSERT = 1 << 6, /* go to insert mode */
MEC_KILL = MEC_COPY | MEC_DELETE,
MEC_CHANGE = MEC_DELETE | MEC_INSERT,
MEC_COPYCHANGE = MEC_KILL | MEC_INSERT,
};
/* The state in which a command is executed. */
struct state_T {
struct {
/* When count is not specified, `sign' and `abs' are 0.
* Otherwise, `sign' is 1 or -1.
* When the negative sign is specified but digits are not, `abs' is 0.*/
int sign;
unsigned abs;
int multiplier;
} count;
enum motion_expect_command_T pending_command_motion;
le_command_func_T *pending_command_char;
};
#define COUNT_ABS_MAX 999999999
/* The current state. */
static struct state_T state;
/* The last executed editing command and the then state.
* Valid iff `.command.func' is non-null. */
static struct {
struct le_command_T command;
struct state_T state;
} last_edit_command;
/* The last executed find/till command. */
/* `last_find_command' is valid iff `.func' is non-null. */
static struct le_command_T last_find_command;
/* The editing mode before the mode is changed to LE_MODE_CHAR_EXPECT/SEARCH.
* When the char-expecting/search command finishes, the mode is restored to
* this mode. */
static le_mode_id_T savemode;
/* When starting the overwrite mode, the then `le_main_buffer' contents and
* `le_main_length' are saved in this structure. The values are kept so that the
* original contents can be restored when the user hits backspace. When the user
* leaves the overwrite mode, `contents' is freed and set to NULL. */
static struct {
wchar_t *contents;
size_t length;
} overwrite_save_buffer;
/* History of the edit line between editing commands. */
static plist_T undo_history;
/* Index of the current state in the history.
* If the current state is the newest, the index is `undo_history.length'. */
static size_t undo_index;
/* The history entry that is saved in the undo history. */
static const histlink_T *undo_history_entry;
/* The index that is to be the value of the `index' member of the next undo
* history entry. */
static size_t undo_save_index;
/* Structure of history entries */
struct undo_history {
size_t index; /* index of the cursor */
wchar_t contents[]; /* contents of the edit line */
// `contents' is a copy of `le_main_buffer.contents' up to `le_main_length'.
};
#define KILL_RING_SIZE 32 /* must be power of 2 */
/* The kill ring */
static wchar_t *kill_ring[KILL_RING_SIZE];
/* The index of the element to which next killed string is assigned. */
static size_t next_kill_index = 0;
/* The index of the last put element. */
static size_t last_put_elem = 0; /* < KILL_RING_SIZE */
/* The position and length of the last put string. */
static size_t last_put_range_start, last_put_range_length;
/* Set to true if the next completion command should restart completion from
* scratch. */
static bool reset_completion;
/* The next value of `reset_completion'. */
static bool next_reset_completion;
/* Probability distribution tree for command prediction. */
static trie_T *prediction_tree = NULL;
static void reset_state(void);
static void reset_count(void);
static int get_count(int default_value)
__attribute__((pure));
static size_t active_length(void)
__attribute__((pure));
static void save_current_edit_command(void);
static void save_current_find_command(void);
static void save_undo_history(void);
static void maybe_save_undo_history(void);
static void exec_motion_command(size_t new_index, bool inclusive);
static void set_motion_expect_command(enum motion_expect_command_T cmd);
static void exec_motion_expect_command(
enum motion_expect_command_T cmd, le_command_func_T motion);
static void exec_motion_expect_command_line(enum motion_expect_command_T cmd);
static void exec_motion_expect_command_all(void);
static void add_to_kill_ring(const wchar_t *s, size_t n)
__attribute__((nonnull));
static void set_char_expect_command(le_command_func_T cmd)
__attribute__((nonnull));
static void set_overwriting(bool overwriting);
static inline bool is_overwriting(void)
__attribute__((pure));
static void restore_overwritten_buffer_contents(
size_t start_index, size_t end_index);
static void set_search_mode(le_mode_id_T mode, enum le_search_direction_T dir,
bool init_le);
static void to_upper_case(wchar_t *s, size_t n)
__attribute__((nonnull));
static void to_lower_case(wchar_t *s, size_t n)
__attribute__((nonnull));
static void switch_case(wchar_t *s, size_t n)
__attribute__((nonnull));
static void set_mode(le_mode_id_T newmode, bool overwrite);
static void redraw_all(bool clear);
static bool alert_if_first(void);
static bool alert_if_last(void);
static void move_cursor_forward_char(int offset);
static void move_cursor_backward_char(int offset);
static void move_cursor_forward_bigword(int count);
static void move_cursor_backward_bigword(int count);
static size_t next_bigword_index(const wchar_t *s, size_t i)
__attribute__((nonnull));
static size_t next_end_of_bigword_index(
const wchar_t *s, size_t i, bool progress)
__attribute__((nonnull));
static size_t previous_bigword_index(const wchar_t *s, size_t i)
__attribute__((nonnull));
static void move_cursor_forward_semiword(int count);
static void move_cursor_backward_semiword(int count);
static size_t next_semiword_index(const wchar_t *s, size_t i)
__attribute__((nonnull));
static size_t next_end_of_semiword_index(
const wchar_t *s, size_t i, bool progress)
__attribute__((nonnull));
static size_t previous_semiword_index(const wchar_t *s, size_t i)
__attribute__((nonnull));
static void move_cursor_forward_viword(int count);
static inline bool need_cw_treatment(void)
__attribute__((pure));
static void move_cursor_backward_viword(int count);
static size_t next_viword_index(const wchar_t *s, size_t i)
__attribute__((nonnull));
static size_t next_end_of_viword_index(
const wchar_t *s, size_t i, bool progress)
__attribute__((nonnull));
static size_t previous_viword_index(const wchar_t *s, size_t i)
__attribute__((nonnull));
static void move_cursor_forward_emacsword(int count);
static void move_cursor_backward_emacsword(int count);
static size_t next_emacsword_index(const wchar_t *s, size_t i)
__attribute__((nonnull));
static size_t previous_emacsword_index(const wchar_t *s, size_t i)
__attribute__((nonnull));
static void find_char(wchar_t c);
static void find_char_rev(wchar_t c);
static void till_char(wchar_t c);
static void till_char_rev(wchar_t c);
static void exec_find(wchar_t c, int count, bool till);
static size_t find_nth_occurence(wchar_t c, int n);
static void put_killed_string(bool after_cursor, bool cursor_on_last_char);
static void insert_killed_string(
bool after_cursor, bool cursor_on_last_char, size_t index);
static void cancel_undo(int offset);
static void check_reset_completion(void);
static void create_prediction_tree(void);
static size_t count_matching_previous_commands(const histentry_T *e1)
__attribute__((nonnull,pure));
static void clear_prediction(void);
static void update_buffer_with_prediction(void);
static void vi_replace_char(wchar_t c);
static void vi_exec_alias(wchar_t c);
struct xwcsrange { const wchar_t *start, *end; };
static struct xwcsrange get_next_bigword(const wchar_t *s)
__attribute__((nonnull));
static struct xwcsrange get_prev_bigword(
const wchar_t *beginning, const wchar_t *s)
__attribute__((nonnull));
static void replace_horizontal_space(bool deleteafter, const wchar_t *s)
__attribute__((nonnull));
static void go_to_history_absolute(
const histlink_T *l, enum le_search_type_T curpos)
__attribute__((nonnull));
static void go_to_history_relative(int offset, enum le_search_type_T curpos);
static void go_to_history(const histlink_T *l, enum le_search_type_T curpos)
__attribute__((nonnull));
static bool need_update_last_search_value(void)
__attribute__((pure));
static void update_search(void);
static void perform_search(const wchar_t *pattern,
enum le_search_direction_T dir, enum le_search_type_T type)
__attribute__((nonnull));
static void search_again(enum le_search_direction_T dir);
static void beginning_search(enum le_search_direction_T dir);
static inline bool beginning_search_check_go_to_history(const wchar_t *prefix)
__attribute__((nonnull,pure));
#define ALERT_AND_RETURN_IF_PENDING \
do if (state.pending_command_motion != MEC_MOVE) \
{ cmd_alert(L'\0'); return; } \
while (0)
/* Initializes the editing module before starting editing. */
void le_editing_init(void)
{
wb_init(&le_main_buffer);
le_main_length = le_main_index = 0;
main_history_entry = Histlist;
main_history_value = xwcsdup(L"");
switch (shopt_lineedit) {
case SHOPT_VI: le_set_mode(LE_MODE_VI_INSERT); break;
case SHOPT_EMACS: le_set_mode(LE_MODE_EMACS); break;
default: UNREACHABLE();
}
last_command.func = 0;
last_command.arg = L'\0';
start_using_history();
pl_init(&undo_history);
undo_index = 0;
undo_save_index = le_main_index;
undo_history_entry = Histlist;
save_undo_history();
reset_completion = true;
reset_state();
set_overwriting(false);
if (shopt_le_predict) {
create_prediction_tree();
update_buffer_with_prediction();
}
}
/* Finalizes the editing module when editing is finished.
* Returns the content of the main buffer, which must be freed by the caller. */
wchar_t *le_editing_finalize(void)
{
assert(le_search_buffer.contents == NULL);
plfree(pl_toary(&undo_history), free);
le_complete_cleanup();
end_using_history();
free(main_history_value);
clear_prediction();
trie_destroy(prediction_tree), prediction_tree = NULL;
wb_wccat(&le_main_buffer, L'\n');
return wb_towcs(&le_main_buffer);
}
/* Invokes the specified command. */
void le_invoke_command(le_command_func_T *cmd, wchar_t arg)
{
current_command.func = cmd;
current_command.arg = arg;
next_reset_completion = true;
cmd(arg);
last_command = current_command;
reset_completion |= next_reset_completion;
if (le_main_length < le_main_index)
le_main_length = le_main_index;
switch (le_editstate) {
case LE_EDITSTATE_EDITING:
if (shopt_le_predict)
update_buffer_with_prediction();
break;
case LE_EDITSTATE_DONE:
case LE_EDITSTATE_ERROR:
clear_prediction();
break;
case LE_EDITSTATE_INTERRUPTED:
break;
}
if (LE_CURRENT_MODE == LE_MODE_VI_COMMAND)
if (le_main_index > 0 && le_main_index == le_main_buffer.length)
le_main_index--;
}
/* Resets `state'. */
void reset_state(void)
{
reset_count();
state.pending_command_motion = MEC_MOVE;
state.pending_command_char = 0;
}
/* Resets `state.count'. */
void reset_count(void)
{
state.count.sign = 0;
state.count.abs = 0;
state.count.multiplier = 1;
}
/* Returns the count value.
* If the count is not set, returns the `default_value'. */
int get_count(int default_value)
{
long long result;
if (state.count.sign == 0)
result = (long long) default_value * state.count.multiplier;
else if (state.count.sign < 0 && state.count.abs == 0)
result = (long long) -state.count.multiplier;
else
result = (long long) state.count.abs * state.count.sign *
state.count.multiplier;
if (result < -COUNT_ABS_MAX)
result = -COUNT_ABS_MAX;
else if (result > COUNT_ABS_MAX)
result = COUNT_ABS_MAX;
return result;
}
/* Returns the length of the first part of `le_main_buffer'. */
size_t active_length(void)
{
if (le_main_length > le_main_buffer.length)
return le_main_buffer.length;
if (le_main_length < le_main_index)
return le_main_index;
return le_main_length;
}
/* Saves the currently executing command and the current state in
* `last_edit_command' if we are not redoing and the mode is not "vi insert". */
void save_current_edit_command(void)
{
if (current_command.func != cmd_redo
&& LE_CURRENT_MODE != LE_MODE_VI_INSERT) {
last_edit_command.command = current_command;
last_edit_command.state = state;
}
}
/* Saves the currently executing command and the current state in
* `last_find_command' if we are not redoing/refinding. */
void save_current_find_command(void)
{
if (current_command.func != cmd_refind_char
&& current_command.func != cmd_refind_char_rev
&& current_command.func != cmd_redo)
last_find_command = current_command;
}
/* Saves the current contents of the edit line to the undo history.
* History entries at the current `undo_index' and newer are removed before
* saving the current. If `undo_history_entry' is different from
* `main_history_entry', all undo history entries are removed. */
void save_undo_history(void)
{
for (size_t i = undo_index; i < undo_history.length; i++)
free(undo_history.contents[i]);
pl_truncate(&undo_history, undo_index);
// No need to check for overflow in `len + 1' here. Should overflow occur,
// the buffer would not have been allocated successfully.
size_t len = active_length();
struct undo_history *e = xmallocs(sizeof *e, len + 1, sizeof *e->contents);
e->index = le_main_index;
wcsncpy(e->contents, le_main_buffer.contents, len);
e->contents[len] = L'\0';
pl_add(&undo_history, e);
assert(undo_index == undo_history.length - 1);
undo_history_entry = main_history_entry;
}
/* Calls `save_undo_history' if the current contents of the edit line is not
* saved. */
void maybe_save_undo_history(void)
{
assert(undo_index <= undo_history.length);
size_t save_undo_save_index = undo_save_index;
undo_save_index = le_main_index;
size_t len = active_length();
if (undo_history_entry == main_history_entry) {
if (undo_index < undo_history.length) {
struct undo_history *h = undo_history.contents[undo_index];
if (wcsncmp(le_main_buffer.contents, h->contents, len) == 0 &&
h->contents[len] == L'\0') {
/* The contents of the main buffer is the same as saved in the
* history. Just save the index. */
h->index = le_main_index;
return;
}
undo_index++;
}
} else {
if (wcsncmp(le_main_buffer.contents, main_history_value, len) == 0 &&
main_history_value[len] == L'\0')
return;
/* The contents of the buffer has been changed from the value of the
* history entry, but it's not yet saved in the undo history. We first
* save the original history value and then save the current buffer
* contents. */
struct undo_history *h;
pl_clear(&undo_history, free);
h = xmallocs(sizeof *h,
add(wcslen(main_history_value), 1), sizeof *h->contents);
assert(save_undo_save_index <= wcslen(main_history_value));
h->index = save_undo_save_index;
wcscpy(h->contents, main_history_value);
pl_add(&undo_history, h);
undo_index = 1;
}
save_undo_history();
}
/* Applies the currently pending editing command to the range between the
* current cursor index and the specified index. If no editing command is
* pending, simply moves the cursor to the specified index. */
/* This function is used for all cursor-moving commands, even when not in the
* vi mode. */
void exec_motion_command(size_t new_index, bool inclusive)
{
assert(le_main_index <= le_main_buffer.length);
assert(new_index <= le_main_buffer.length);
size_t old_index = le_main_index;
size_t start_index, end_index;
if (old_index <= new_index)
start_index = old_index, end_index = new_index;
else
start_index = new_index, end_index = old_index;
if (inclusive && end_index < le_main_buffer.length)
end_index++;
enum motion_expect_command_T mec = state.pending_command_motion;
/* don't save undo history when repeating backspace */
bool repeated_backspace = (mec & MEC_DELETE)
&& (new_index + 1 == old_index)
&& current_command.func == cmd_backward_delete_char
&& last_command.func == cmd_backward_delete_char;
if (!repeated_backspace)
maybe_save_undo_history();
if (mec & MEC_COPY) {
add_to_kill_ring(&le_main_buffer.contents[start_index],
end_index - start_index);
}
if (mec & MEC_CASEMASK) {
void (*case_func)(wchar_t *, size_t) DUMMY_INIT(0);
switch (mec & MEC_CASEMASK) {
case MEC_UPPERCASE:
case_func = to_upper_case;
break;
case MEC_LOWERCASE:
case_func = to_lower_case;
break;
case MEC_SWITCHCASE:
case_func = switch_case;
break;
}
case_func(&le_main_buffer.contents[start_index],
end_index - start_index);
if (le_main_length < end_index)
le_main_length = end_index;
}
switch (mec & MEC_CURSORMASK) {
case MEC_TOSTART: le_main_index = start_index; break;
case MEC_TOEND: le_main_index = end_index; break;
case MEC_MOVE: le_main_index = new_index; break;
}
if (mec & MEC_DELETE) {
save_current_edit_command();
clear_prediction();
if (!is_overwriting() || old_index <= new_index)
wb_remove(&le_main_buffer, start_index, end_index - start_index);
else
restore_overwritten_buffer_contents(start_index, end_index);
le_main_index = start_index;
}
if (mec & MEC_INSERT) {
le_set_mode(LE_MODE_VI_INSERT);
set_overwriting(false);
}
reset_state();
}
/* Sets the specified motion expecting command as pending.
* If the command is already pending, the command is executed on the whole
* line. */
void set_motion_expect_command(enum motion_expect_command_T cmd)
{
if (state.pending_command_motion == MEC_MOVE) {
state.count.multiplier = get_count(1);
state.count.sign = 0;
state.count.abs = 0;
state.pending_command_motion = cmd;
} else {
if (state.pending_command_motion == cmd)
exec_motion_expect_command_all();
else
cmd_alert(L'\0');
}
}
/* Executes the specified motion expecting command with the specified motion
* command. */
void exec_motion_expect_command(
enum motion_expect_command_T cmd, le_command_func_T motion)
{
if (current_command.func != cmd_redo)
ALERT_AND_RETURN_IF_PENDING;
state.pending_command_motion = cmd;
motion(L'\0');
}
/* Executes the specified motion expecting command from the beginning to the end
* of the line. */
void exec_motion_expect_command_line(enum motion_expect_command_T cmd)
{
if (current_command.func != cmd_redo)
ALERT_AND_RETURN_IF_PENDING;
state.pending_command_motion = cmd;
exec_motion_expect_command_all();
}
/* Executes the currently pending motion expecting command from the beginning to
* the end of the line. */
void exec_motion_expect_command_all(void)
{
size_t save_index = le_main_index;
enum motion_expect_command_T save_pending = state.pending_command_motion;
le_main_index = 0;
cmd_end_of_line(L'\0');
if (!(save_pending & (MEC_DELETE | MEC_CURSORMASK)))
le_main_index = save_index;
}
/* Adds the specified string to the kill ring.
* The maximum number of characters that are added is specified by `n'. */
void add_to_kill_ring(const wchar_t *s, size_t n)
{
if (n > 0 && s[0] != L'\0') {
free(kill_ring[next_kill_index]);
kill_ring[next_kill_index] = xwcsndup(s, n);
next_kill_index = (next_kill_index + 1) % KILL_RING_SIZE;
}
}
/* Sets the editing mode to "char expect" and the pending command to `cmd'.
* The current editing mode is saved in `savemode'. */
void set_char_expect_command(le_command_func_T cmd)
{
savemode = LE_CURRENT_MODE;
le_set_mode(LE_MODE_CHAR_EXPECT);
state.pending_command_char = cmd;
}
/* Enables or disables the overwrite mode. */
void set_overwriting(bool overwrite)
{
free(overwrite_save_buffer.contents);
if (overwrite) {
size_t len = active_length();
overwrite_save_buffer.contents = xwcsndup(le_main_buffer.contents, len);
overwrite_save_buffer.length = len;
} else {
overwrite_save_buffer.contents = NULL;
}
}
/* Returns true iff the overwrite mode is active. */
bool is_overwriting(void)
{
return overwrite_save_buffer.contents != NULL;
}
/* Restores the main buffer contents that were overwritten in the current
* overwrite mode. When called, `le_main_length >= le_main_buffer.length' must
* hold. The caller must adjust `le_main_index' because this function may remove
* some characters from `le_main_buffer'. */
void restore_overwritten_buffer_contents(size_t start_index, size_t end_index)
{
size_t mid_index;
if (overwrite_save_buffer.length < start_index)
mid_index = start_index;
else if (overwrite_save_buffer.length > end_index)
mid_index = end_index;
else
mid_index = overwrite_save_buffer.length;
/* Restore contents from `start_index' to `mid_index' */
wmemcpy(&le_main_buffer.contents[start_index],
&overwrite_save_buffer.contents[start_index],
mid_index - start_index);
/* Contents from `mid_index' to `end_index' were actually not overwritten
* but appended, so they should be removed. */
wb_remove(&le_main_buffer, mid_index, end_index - mid_index);
}
/* Starts command history search by setting the editing mode to `mode' with
* the specified direction `dir'. `mode' must be either LE_MODE_VI_SEARCH or
* LE_MODE_EMACS_SEARCH.
*
* If `init_le` is true, the search buffer is initialised with the current
* content of the line-edit buffer.
*
* The current editing mode is saved in `savemode'. */
void set_search_mode(le_mode_id_T mode, enum le_search_direction_T dir,
bool init_le)
{
le_complete_cleanup();
savemode = LE_CURRENT_MODE;
le_set_mode(mode);
le_search_direction = dir;
switch (mode) {
case LE_MODE_VI_SEARCH: le_search_type = SEARCH_VI; break;
case LE_MODE_EMACS_SEARCH: le_search_type = SEARCH_EMACS; break;
default: UNREACHABLE();
}
wb_init(&le_search_buffer);
if (init_le) {
wb_ncat(&le_search_buffer, le_main_buffer.contents, active_length());
}
update_search();
}
/* Converts the first `n' characters of string `s' to upper case.
* The string must be at least `n' characters long. */
void to_upper_case(wchar_t *s, size_t n)
{
for (size_t i = 0; i < n; i++)
s[i] = towupper(s[i]);
}
/* Converts the first `n' characters of string `s' to lower case.
* The string must be at least `n' characters long. */
void to_lower_case(wchar_t *s, size_t n)
{
for (size_t i = 0; i < n; i++)
s[i] = towlower(s[i]);
}
/* Switches case of the first `n' characters of string `s'.
* The string must be at least `n' characters long. */
void switch_case(wchar_t *s, size_t n)
{
for (size_t i = 0; i < n; i++) {
wchar_t c = s[i];
s[i] = iswlower(c) ? towupper(c) : towlower(c);
}
}
/********** Basic Commands **********/
/* Does nothing. */
void cmd_noop(wchar_t c __attribute__((unused)))
{
next_reset_completion = false;
reset_state();
}
/* Alerts. */
void cmd_alert(wchar_t c __attribute__((unused)))
{
lebuf_print_alert(true);
reset_state();
}
/* Inserts the character argument into the buffer.
* If the count is set, inserts `count' times.
* If `is_overwriting()' is true, overwrites the character instead of inserting.
*/
void cmd_self_insert(wchar_t c)
{
ALERT_AND_RETURN_IF_PENDING;
if (c == L'\0') {
cmd_alert(L'\0');
return;
}
clear_prediction();
int count = get_count(1);
while (--count >= 0)
if (is_overwriting() && le_main_index < le_main_buffer.length)
le_main_buffer.contents[le_main_index++] = c;
else
wb_ninsert_force(&le_main_buffer, le_main_index++, &c, 1);
reset_state();
}
/* Inserts the tab character. */
void cmd_insert_tab(wchar_t c __attribute__((unused)))
{
cmd_self_insert(L'\t');
}
/* Sets the `le_next_verbatim' flag.
* The next character will be input to the main buffer even if it's a special
* character. */
void cmd_expect_verbatim(wchar_t c __attribute__((unused)))
{
le_next_verbatim = true;
}
/* Adds the specified digit to the accumulating argument. */
/* If `c' is not a digit or a hyphen, does nothing. */
void cmd_digit_argument(wchar_t c)
{
if (L'0' <= c && c <= L'9') {
if (state.count.abs > COUNT_ABS_MAX / 10) {
cmd_alert(L'\0'); // argument too large
return;
}
if (state.count.sign == 0)
state.count.sign = 1;
state.count.abs = state.count.abs * 10 + (unsigned) (c - L'0');
} else if (c == L'-') {
if (state.count.sign == 0)
state.count.sign = -1;
else
state.count.sign = -state.count.sign;
}
next_reset_completion = false;
}
/* If the count is not set, moves the cursor to the beginning of the line.
* Otherwise, adds the given digit to the count. */
void cmd_bol_or_digit(wchar_t c)
{
if (state.count.sign == 0)
cmd_beginning_of_line(c);
else
cmd_digit_argument(c);
}
/* Accepts the current line.
* `le_editstate' is set to LE_EDITSTATE_DONE to induce line-editing to
* terminate.
* If history search is currently active, the search result is accepted. If the
* search was failing, the line is not accepted. */
void cmd_accept_line(wchar_t c __attribute__((unused)))
{
ALERT_AND_RETURN_IF_PENDING;
if (le_search_buffer.contents == NULL) {
le_editstate = LE_EDITSTATE_DONE;
reset_state();
} else {
if (le_search_result != Histlist)
le_editstate = LE_EDITSTATE_DONE;
cmd_srch_accept_search(L'\0');
}
}
/* Aborts the current line.
* `le_editstate' is set to LE_EDITSTATE_INTERRUPTED to induce line-editing to
* terminate. */
void cmd_abort_line(wchar_t c __attribute__((unused)))
{
cmd_srch_abort_search(L'\0');
le_editstate = LE_EDITSTATE_INTERRUPTED;
reset_state();
}
/* Sets `le_editstate' to LE_EDITSTATE_ERROR.
* The `le_readline' function will return INPUT_EOF. */
void cmd_eof(wchar_t c __attribute__((unused)))
{
ALERT_AND_RETURN_IF_PENDING;
cmd_srch_abort_search(L'\0');
le_editstate = LE_EDITSTATE_ERROR;
reset_state();
}
/* If the edit line is empty, sets `le_editstate' to LE_EDITSTATE_ERROR (return
* EOF). Otherwise, alerts. */
void cmd_eof_if_empty(wchar_t c __attribute__((unused)))
{
if (active_length() == 0)
cmd_eof(L'\0');
else
cmd_alert(L'\0');
}
/* If the edit line is empty, sets `le_editstate' to LE_EDITSTATE_ERROR (return
* EOF). Otherwise, deletes the character under the cursor. */
void cmd_eof_or_delete(wchar_t c __attribute__((unused)))
{
if (active_length() == 0)
cmd_eof(L'\0');
else
cmd_delete_char(L'\0');
}
/* Inserts a hash sign ('#') at the beginning of the line and accepts the line.
* If any count is set and the line already begins with a hash sign, the hash
* sign is removed rather than added. The line is accepted anyway. */
void cmd_accept_with_hash(wchar_t c __attribute__((unused)))
{
ALERT_AND_RETURN_IF_PENDING;
clear_prediction();
if (state.count.sign == 0 || le_main_buffer.contents[0] != L'#')
wb_insert(&le_main_buffer, 0, L"#");
else
wb_remove(&le_main_buffer, 0, 1);
le_main_index = 0;
cmd_accept_line(L'\0');
}
/* Accept the current line including the prediction. */
void cmd_accept_prediction(wchar_t c)
{
cmd_accept_line(c);
if (le_editstate == LE_EDITSTATE_DONE)
le_main_length = SIZE_MAX;
}
/* Changes the editing mode to "vi insert". */
void cmd_setmode_viinsert(wchar_t c __attribute__((unused)))
{
set_mode(LE_MODE_VI_INSERT, false);
}
/* Changes the editing mode to "vi command". */
void cmd_setmode_vicommand(wchar_t c __attribute__((unused)))
{
set_mode(LE_MODE_VI_COMMAND, false);
}
/* Changes the editing mode to "emacs". */
void cmd_setmode_emacs(wchar_t c __attribute__((unused)))
{
set_mode(LE_MODE_EMACS, false);
}
/* Changes the editing mode to the specified one. */
void set_mode(le_mode_id_T newmode, bool overwrite)
{
ALERT_AND_RETURN_IF_PENDING;
maybe_save_undo_history();
if (LE_CURRENT_MODE == LE_MODE_VI_INSERT && newmode == LE_MODE_VI_COMMAND)
if (le_main_index > 0)