4187119d |
1 | /* vi_mode.c -- A vi emulation mode for Bash. |
2 | Mostly written by Jeff Sparkes (jeff1@????). |
3 | */ |
4 | |
5 | \f |
6 | /* **************************************************************** */ |
7 | /* */ |
8 | /* VI Emulation Mode */ |
9 | /* */ |
10 | /* **************************************************************** */ |
11 | |
12 | /* Last string searched for from `/' or `?'. */ |
13 | static char *vi_last_search = (char *)NULL; |
14 | static int vi_histpos; |
15 | |
4187119d |
16 | /* *** UNCLEAN *** */ |
17 | /* Command keys which do movement for xxx_to commands. */ |
18 | static char *vi_motion = " hl^$0ftFt;,%wbeWBE|"; |
19 | |
20 | /* Keymap used for vi replace characters. Created dynamically since |
21 | rarely used. */ |
22 | static Keymap vi_replace_map = (Keymap)NULL; |
23 | |
24 | /* The number of characters inserted in the last replace operation. */ |
25 | static vi_replace_count = 0; |
26 | |
27 | /* Yank the nth arg from the previous line into this line at point. */ |
28 | rl_vi_yank_arg (count) |
29 | int count; |
30 | { |
31 | rl_yank_nth_arg (count); |
32 | } |
33 | |
34 | /* Search again for the last thing searched for. */ |
35 | rl_vi_search_again (ignore, key) |
36 | int ignore, key; |
37 | { |
38 | switch (key) |
39 | { |
40 | case 'n': |
41 | rl_vi_dosearch (vi_last_search, -1); |
42 | break; |
43 | |
44 | case 'N': |
45 | rl_vi_dosearch (vi_last_search, 1); |
46 | break; |
47 | } |
48 | } |
49 | |
50 | /* Do a vi style search. */ |
51 | rl_vi_search (count, key) |
52 | int count, key; |
53 | { |
54 | int dir, c; |
55 | char *p; |
56 | |
57 | switch (key) |
58 | { |
59 | case '?': |
60 | dir = 1; |
61 | break; |
62 | |
63 | case '/': |
64 | dir = -1; |
65 | break; |
66 | |
67 | default: |
68 | ding (); |
69 | return; |
70 | } |
71 | |
72 | vi_histpos = where_history (); |
73 | maybe_save_line (); |
74 | |
75 | /* Reuse the line input buffer to read the search string. */ |
76 | the_line[0] = 0; |
77 | rl_end = rl_point = 0; |
78 | p = (char *)alloca (2 + rl_prompt ? strlen (rl_prompt) : 0); |
79 | |
80 | sprintf (p, "%s%c", rl_prompt ? rl_prompt : "", key); |
81 | |
82 | rl_message (p); |
83 | |
84 | while (c = rl_read_key (in_stream)) |
85 | { |
86 | switch (c) |
87 | { |
88 | case CTRL('W'): |
89 | case CTRL('U'): |
90 | case CTRL('H'): |
91 | case RUBOUT: |
92 | rl_dispatch (c, keymap); |
93 | break; |
94 | |
95 | case ESC: |
96 | case RETURN: |
97 | case NEWLINE: |
98 | goto dosearch; |
99 | break; |
100 | |
101 | case CTRL('C'): |
102 | maybe_unsave_line (); |
103 | rl_clear_message (); |
104 | rl_point = 0; |
105 | ding (); |
106 | return; |
107 | |
108 | default: |
109 | rl_insert (1, c); |
110 | break; |
111 | } |
112 | rl_redisplay (); |
113 | } |
114 | dosearch: |
115 | if (vi_last_search) |
116 | free (vi_last_search); |
117 | |
118 | vi_last_search = savestring (the_line); |
119 | rl_vi_dosearch (the_line, dir); |
120 | } |
121 | |
122 | rl_vi_dosearch (string, dir) |
123 | char *string; |
124 | int dir; |
125 | { |
126 | int old, save = vi_histpos; |
127 | HIST_ENTRY *h; |
128 | |
129 | if (string == 0 || *string == 0 || vi_histpos < 0) |
130 | { |
131 | ding (); |
132 | return; |
133 | } |
134 | |
135 | if ((save = history_search_pos (string, dir, vi_histpos + dir)) == -1) |
136 | { |
137 | maybe_unsave_line (); |
138 | rl_clear_message (); |
139 | rl_point = 0; |
140 | ding (); |
141 | return; |
142 | } |
143 | |
144 | vi_histpos = save; |
145 | |
146 | old = where_history (); |
147 | history_set_pos (vi_histpos); |
148 | h = current_history (); |
149 | history_set_pos (old); |
150 | |
151 | strcpy (the_line, h->line); |
152 | rl_undo_list = (UNDO_LIST *)h->data; |
153 | rl_end = strlen (the_line); |
154 | rl_point = 0; |
155 | rl_clear_message (); |
156 | } |
157 | |
158 | /* Completion, from vi's point of view. */ |
159 | rl_vi_complete (ignore, key) |
160 | int ignore, key; |
161 | { |
162 | if (!whitespace (the_line[rl_point])) |
163 | { |
164 | rl_vi_end_word (1, 'E'); |
165 | rl_point++; |
166 | } |
167 | rl_complete_internal ('*'); |
168 | rl_vi_insertion_mode (); |
169 | } |
170 | |
171 | /* Previous word in vi mode. */ |
172 | rl_vi_prev_word (count, key) |
173 | int count, key; |
174 | { |
175 | if (count < 0) |
176 | { |
177 | rl_vi_next_word (-count, key); |
178 | return; |
179 | } |
180 | |
181 | if (uppercase_p (key)) |
182 | rl_vi_bWord (count); |
183 | else |
184 | rl_vi_bword (count); |
185 | } |
186 | |
187 | /* Next word in vi mode. */ |
188 | rl_vi_next_word (count, key) |
189 | int count; |
190 | { |
191 | if (count < 0) |
192 | { |
193 | rl_vi_prev_word (-count, key); |
194 | return; |
195 | } |
196 | |
197 | if (uppercase_p (key)) |
198 | rl_vi_fWord (count); |
199 | else |
200 | rl_vi_fword (count); |
201 | } |
202 | |
203 | /* Move to the end of the ?next? word. */ |
204 | rl_vi_end_word (count, key) |
205 | int count, key; |
206 | { |
207 | if (count < 0) |
208 | { |
209 | ding (); |
210 | return; |
211 | } |
212 | |
213 | if (uppercase_p (key)) |
214 | rl_vi_eWord (count); |
215 | else |
216 | rl_vi_eword (count); |
217 | } |
218 | |
219 | /* Move forward a word the way that 'W' does. */ |
220 | rl_vi_fWord (count) |
221 | int count; |
222 | { |
223 | while (count-- && rl_point < (rl_end - 1)) |
224 | { |
225 | /* Skip until whitespace. */ |
226 | while (!whitespace (the_line[rl_point]) && rl_point < rl_end) |
227 | rl_point++; |
228 | |
229 | /* Now skip whitespace. */ |
230 | while (whitespace (the_line[rl_point]) && rl_point < rl_end) |
231 | rl_point++; |
232 | } |
233 | } |
234 | |
235 | rl_vi_bWord (count) |
236 | int count; |
237 | { |
238 | while (count-- && rl_point > 0) |
239 | { |
240 | while (rl_point-- >= 0 && whitespace (the_line[rl_point])); |
241 | while (rl_point >= 0 && !whitespace (the_line[rl_point])) |
242 | rl_point--; |
243 | rl_point++; |
244 | } |
245 | } |
246 | |
247 | rl_vi_eWord (count) |
248 | int count; |
249 | { |
250 | while (count -- && rl_point < (rl_end - 1)) |
251 | { |
252 | while (rl_point++ < rl_end && whitespace (the_line[rl_point])); |
253 | while (rl_point++ < rl_end && !whitespace (the_line[rl_point])); |
254 | rl_point--; |
255 | } |
256 | } |
257 | |
258 | rl_vi_fword (count) |
259 | int count; |
260 | { |
261 | while (count -- && rl_point < (rl_end - 1)) |
262 | { |
263 | if (isident (the_line[rl_point])) |
264 | { |
265 | while (isident (the_line[rl_point]) && rl_point < rl_end) |
266 | rl_point += 1; |
267 | } |
268 | else if (!whitespace (the_line[rl_point])) |
269 | { |
270 | while (!isident (the_line[rl_point]) && |
271 | !whitespace (the_line[rl_point]) && rl_point < rl_end) |
272 | rl_point += 1; |
273 | } |
274 | |
275 | while (whitespace (the_line[rl_point]) && rl_point < rl_end) |
276 | rl_point++; |
277 | } |
278 | } |
279 | |
280 | rl_vi_bword (count) |
281 | int count; |
282 | { |
283 | while (count -- && rl_point > 0) |
284 | { |
285 | while (--rl_point > 0 && whitespace (the_line[rl_point])); |
286 | if (rl_point > 0) |
287 | { |
288 | if (isident (the_line[rl_point])) |
289 | while (--rl_point >= 0 && isident (the_line[rl_point])); |
290 | else |
291 | while (--rl_point >= 0 && !isident (the_line[rl_point]) && |
292 | !whitespace (the_line[rl_point])); |
293 | rl_point++; |
294 | } |
295 | } |
296 | } |
297 | |
298 | rl_vi_eword (count) |
299 | int count; |
300 | { |
301 | while (count -- && rl_point < rl_end - 1) |
302 | { |
303 | while (++rl_point < rl_end && whitespace (the_line[rl_point])); |
304 | |
305 | if (rl_point < rl_end) |
306 | { |
307 | if (isident (the_line[rl_point])) |
308 | while (++rl_point < rl_end && isident (the_line[rl_point])); |
309 | else |
310 | while (++rl_point < rl_end && !isident (the_line[rl_point]) |
311 | && !whitespace (the_line[rl_point])); |
312 | rl_point--; |
313 | } |
314 | } |
315 | } |
316 | |
317 | rl_vi_insert_beg () |
318 | { |
319 | rl_beg_of_line (); |
320 | rl_vi_insertion_mode (); |
321 | return 0; |
322 | } |
323 | |
324 | rl_vi_append_mode () |
325 | { |
326 | if (rl_point < rl_end) |
327 | rl_point += 1; |
328 | rl_vi_insertion_mode (); |
329 | return 0; |
330 | } |
331 | |
332 | rl_vi_append_eol () |
333 | { |
334 | rl_end_of_line (); |
335 | rl_vi_append_mode (); |
336 | return 0; |
337 | } |
338 | |
339 | /* What to do in the case of C-d. */ |
340 | rl_vi_eof_maybe (count, c) |
341 | int count, c; |
342 | { |
343 | rl_newline (1, '\n'); |
344 | } |
345 | |
346 | /* Insertion mode stuff. */ |
347 | |
348 | /* Switching from one mode to the other really just involves |
349 | switching keymaps. */ |
350 | rl_vi_insertion_mode () |
351 | { |
352 | keymap = vi_insertion_keymap; |
353 | } |
354 | |
355 | rl_vi_movement_mode () |
356 | { |
357 | if (rl_point > 0) |
358 | rl_backward (1); |
359 | |
360 | keymap = vi_movement_keymap; |
361 | if (vi_doing_insert) |
362 | { |
363 | rl_end_undo_group (); |
364 | vi_doing_insert = 0; |
365 | } |
366 | } |
367 | |
368 | rl_vi_arg_digit (count, c) |
369 | int count, c; |
370 | { |
371 | if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg) |
372 | rl_beg_of_line (); |
373 | else |
374 | rl_digit_argument (count, c); |
375 | } |
376 | |
377 | /* Doesn't take an arg count in vi */ |
378 | rl_vi_change_case (ignore1, ignore2) |
379 | int ignore1, ignore2; |
380 | { |
381 | char c = 0; |
382 | |
383 | if (uppercase_p (the_line[rl_point])) |
384 | c = to_lower (the_line[rl_point]); |
385 | else if (lowercase_p (the_line[rl_point])) |
386 | c = to_upper (the_line[rl_point]); |
387 | |
388 | /* Vi is kind of strange here. */ |
389 | if (c) |
390 | { |
391 | rl_begin_undo_group (); |
392 | rl_delete (1); |
393 | rl_insert (1, c); |
394 | rl_end_undo_group (); |
395 | rl_vi_check (); |
396 | } |
397 | else |
398 | rl_forward (1); |
399 | } |
400 | |
401 | rl_vi_put (count, key) |
402 | int count, key; |
403 | { |
404 | if (!uppercase_p (key)) |
405 | rl_forward (1); |
406 | |
407 | rl_yank (); |
408 | rl_backward (1); |
409 | } |
410 | |
411 | rl_vi_check () |
412 | { |
413 | if (rl_point && rl_point == rl_end) |
414 | rl_point--; |
415 | } |
416 | |
417 | rl_vi_column (count) |
418 | { |
419 | if (count > rl_end) |
420 | rl_end_of_line (); |
421 | else |
422 | rl_point = count - 1; |
423 | } |
424 | |
425 | int |
426 | rl_vi_domove () |
427 | { |
428 | int c, save; |
429 | |
430 | rl_mark = rl_point; |
431 | c = rl_read_key (in_stream); |
432 | |
433 | if (!member (c, vi_motion)) |
434 | { |
435 | if (digit (c)) |
436 | { |
437 | save = rl_numeric_arg; |
438 | rl_digit_loop1 (); |
439 | rl_numeric_arg *= save; |
440 | } |
441 | else |
442 | return (-1); |
443 | } |
444 | |
445 | rl_dispatch (c, keymap); |
446 | |
447 | /* No change in position means the command failed. */ |
448 | if (rl_mark == rl_point) |
449 | return (-1); |
450 | |
451 | if ((c == 'w' || c == 'W') && rl_point < rl_end) |
452 | rl_point--; |
453 | |
454 | if (rl_mark < rl_point) |
455 | exchange (rl_point, rl_mark); |
456 | |
457 | return (0); |
458 | } |
459 | |
460 | /* A simplified loop for vi. Don't dispatch key at end. |
461 | Don't recognize minus sign? */ |
462 | rl_digit_loop1 () |
463 | { |
464 | int key, c; |
465 | |
466 | while (1) |
467 | { |
468 | rl_message ("(arg: %d) ", arg_sign * rl_numeric_arg); |
469 | key = c = rl_read_key (); |
470 | |
471 | if (keymap[c].type == ISFUNC && |
472 | keymap[c].function == rl_universal_argument) |
473 | { |
474 | rl_numeric_arg *= 4; |
475 | continue; |
476 | } |
477 | c = UNMETA (c); |
478 | if (numeric (c)) |
479 | { |
480 | if (rl_explicit_arg) |
481 | rl_numeric_arg = (rl_numeric_arg * 10) + (c - '0'); |
482 | else |
483 | rl_numeric_arg = (c - '0'); |
484 | rl_explicit_arg = 1; |
485 | } |
486 | else |
487 | { |
488 | rl_clear_message (); |
489 | rl_stuff_char (key); |
490 | } |
491 | } |
492 | } |
493 | |
494 | rl_vi_delete_to (count, key) |
495 | int count, key; |
496 | { |
497 | if (uppercase_p (key)) |
498 | rl_stuff_char ('$'); |
499 | |
500 | if (rl_vi_domove ()) |
501 | { |
502 | ding (); |
503 | return; |
504 | } |
505 | |
506 | rl_kill_text (rl_point, rl_mark); |
507 | } |
508 | |
509 | rl_vi_change_to (count, key) |
510 | int count, key; |
511 | { |
512 | if (uppercase_p (key)) |
513 | rl_stuff_char ('$'); |
514 | |
515 | if (rl_vi_domove ()) |
516 | { |
517 | ding (); |
518 | return; |
519 | } |
520 | |
521 | rl_begin_undo_group (); |
522 | vi_doing_insert = 1; |
523 | rl_kill_text (rl_point, rl_mark); |
524 | rl_vi_insertion_mode (); |
525 | } |
526 | |
527 | rl_vi_yank_to (count, key) |
528 | int count, key; |
529 | { |
530 | int save = rl_point; |
531 | |
532 | if (uppercase_p (key)) |
533 | rl_stuff_char ('$'); |
534 | |
535 | if (rl_vi_domove ()) |
536 | { |
537 | ding (); |
538 | return; |
539 | } |
540 | |
541 | rl_begin_undo_group (); |
542 | rl_kill_text (rl_point, rl_mark); |
543 | rl_end_undo_group (); |
544 | rl_do_undo (); |
545 | rl_point = save; |
546 | } |
547 | |
548 | rl_vi_delete (count) |
549 | { |
550 | if (rl_point >= rl_end - 1) |
551 | { |
552 | rl_delete (count); |
553 | if (rl_point > 0) |
554 | rl_backward (1); |
555 | } |
556 | else |
557 | rl_delete (count); |
558 | } |
559 | |
560 | /* Turn the current line into a comment in shell history. A ksh function */ |
561 | rl_vi_comment () |
562 | { |
563 | rl_beg_of_line (); |
564 | rl_insert_text (": "); /* # doesn't work in interactive mode */ |
565 | rl_redisplay (); |
566 | rl_newline (1, '\010'); |
567 | } |
568 | |
569 | rl_vi_first_print () |
570 | { |
571 | rl_back_to_indent (); |
572 | } |
573 | |
574 | rl_back_to_indent (ignore1, ignore2) |
575 | int ignore1, ignore2; |
576 | { |
577 | rl_beg_of_line (); |
578 | while (rl_point < rl_end && whitespace (the_line[rl_point])) |
579 | rl_point++; |
580 | } |
581 | |
582 | /* NOTE: it is necessary that opposite directions are inverses */ |
583 | #define FTO 1 /* forward to */ |
584 | #define BTO -1 /* backward to */ |
585 | #define FFIND 2 /* forward find */ |
586 | #define BFIND -2 /* backward find */ |
587 | |
588 | rl_vi_char_search (count, key) |
589 | int count, key; |
590 | { |
591 | static char target; |
592 | static int orig_dir, dir; |
593 | int pos; |
594 | |
595 | if (key == ';' || key == ',') |
596 | dir = (key == ';' ? orig_dir : -orig_dir); |
597 | else |
598 | { |
599 | target = rl_getc (in_stream); |
600 | |
601 | switch (key) |
602 | { |
603 | case 't': |
604 | orig_dir = dir = FTO; |
605 | break; |
606 | |
607 | case 'T': |
608 | orig_dir = dir = BTO; |
609 | break; |
610 | |
611 | case 'f': |
612 | orig_dir = dir = FFIND; |
613 | break; |
614 | |
615 | case 'F': |
616 | orig_dir = dir = BFIND; |
617 | break; |
618 | } |
619 | } |
620 | |
621 | pos = rl_point; |
622 | |
623 | if (dir < 0) |
624 | { |
625 | pos--; |
626 | do |
627 | { |
628 | if (the_line[pos] == target) |
629 | { |
630 | if (dir == BTO) |
631 | rl_point = pos + 1; |
632 | else |
633 | rl_point = pos; |
634 | return; |
635 | } |
636 | } |
637 | while (pos--); |
638 | |
639 | if (pos < 0) |
640 | { |
641 | ding (); |
642 | return; |
643 | } |
644 | } |
645 | else |
646 | { /* dir > 0 */ |
647 | pos++; |
648 | do |
649 | { |
650 | if (the_line[pos] == target) |
651 | { |
652 | if (dir == FTO) |
653 | rl_point = pos - 1; |
654 | else |
655 | rl_point = pos; |
656 | return; |
657 | } |
658 | } |
659 | while (++pos < rl_end); |
660 | |
661 | if (pos >= (rl_end - 1)) |
662 | ding (); |
663 | } |
664 | } |
665 | |
666 | /* Match brackets */ |
667 | rl_vi_match () |
668 | { |
669 | int count = 1, brack, pos; |
670 | |
671 | pos = rl_point; |
672 | if ((brack = rl_vi_bracktype (the_line[rl_point])) == 0) |
673 | { |
674 | while ((brack = rl_vi_bracktype (the_line[rl_point])) == 0 && |
675 | rl_point < rl_end - 1) |
676 | rl_forward (1); |
677 | |
678 | if (brack <= 0) |
679 | { |
680 | rl_point = pos; |
681 | ding (); |
682 | return; |
683 | } |
684 | } |
685 | |
686 | pos = rl_point; |
687 | |
688 | if (brack < 0) |
689 | { |
690 | while (count) |
691 | { |
692 | if (--pos >= 0) |
693 | { |
694 | int b = rl_vi_bracktype (the_line[pos]); |
695 | if (b == -brack) |
696 | count--; |
697 | else if (b == brack) |
698 | count++; |
699 | } |
700 | else |
701 | { |
702 | ding (); |
703 | return; |
704 | } |
705 | } |
706 | } |
707 | else |
708 | { /* brack > 0 */ |
709 | while (count) |
710 | { |
711 | if (++pos < rl_end) |
712 | { |
713 | int b = rl_vi_bracktype (the_line[pos]); |
714 | if (b == -brack) |
715 | count--; |
716 | else if (b == brack) |
717 | count++; |
718 | } |
719 | else |
720 | { |
721 | ding (); |
722 | return; |
723 | } |
724 | } |
725 | } |
726 | rl_point = pos; |
727 | } |
728 | |
729 | int |
730 | rl_vi_bracktype (c) |
731 | int c; |
732 | { |
733 | switch (c) |
734 | { |
735 | case '(': return 1; |
736 | case ')': return -1; |
737 | case '[': return 2; |
738 | case ']': return -2; |
739 | case '{': return 3; |
740 | case '}': return -3; |
741 | default: return 0; |
742 | } |
743 | } |
744 | |
745 | rl_vi_change_char () |
746 | { |
747 | int c; |
748 | |
749 | c = rl_getc (in_stream); |
750 | |
751 | switch (c) |
752 | { |
753 | case '\033': |
754 | case CTRL('C'): |
755 | return; |
756 | |
757 | default: |
758 | rl_begin_undo_group (); |
759 | rl_delete (1); |
760 | rl_insert (1, c); |
761 | rl_end_undo_group (); |
762 | break; |
763 | } |
764 | } |
765 | |
766 | rl_vi_subst (count, key) |
767 | int count, key; |
768 | { |
769 | rl_begin_undo_group (); |
770 | vi_doing_insert = 1; |
771 | |
772 | if (uppercase_p (key)) |
773 | { |
774 | rl_beg_of_line (); |
775 | rl_kill_line (1); |
776 | } |
777 | else |
778 | rl_delete (1); |
779 | |
780 | rl_vi_insertion_mode (); |
781 | } |
782 | |
783 | rl_vi_overstrike (count, key) |
784 | int count, key; |
785 | { |
786 | int i; |
787 | |
788 | if (vi_doing_insert == 0) |
789 | { |
790 | vi_doing_insert = 1; |
791 | rl_begin_undo_group (); |
792 | } |
793 | |
794 | for (i = 0; i < count; i++) |
795 | { |
796 | vi_replace_count++; |
797 | rl_begin_undo_group (); |
798 | |
799 | if (rl_point < rl_end) |
800 | { |
801 | rl_delete (1); |
802 | rl_insert (1, key); |
803 | } |
804 | else |
805 | rl_insert (1, key); |
806 | |
807 | rl_end_undo_group (); |
808 | } |
809 | } |
810 | |
811 | rl_vi_overstrike_delete (count) |
812 | int count; |
813 | { |
814 | int i, s; |
815 | |
816 | for (i = 0; i < count; i++) |
817 | { |
818 | if (vi_replace_count == 0) |
819 | { |
820 | ding (); |
821 | break; |
822 | } |
823 | s = rl_point; |
824 | |
825 | if (rl_do_undo ()) |
826 | vi_replace_count--; |
827 | |
828 | if (rl_point == s) |
829 | rl_backward (1); |
830 | } |
831 | |
832 | if (vi_replace_count == 0 && vi_doing_insert) |
833 | { |
834 | rl_end_undo_group (); |
835 | rl_do_undo (); |
836 | vi_doing_insert = 0; |
837 | } |
838 | } |
839 | |
840 | rl_vi_replace () |
841 | { |
842 | int i; |
843 | |
844 | vi_replace_count = 0; |
845 | |
846 | vi_replace_map = rl_make_bare_keymap (); |
847 | |
848 | for (i = ' '; i < 127; i++) |
849 | vi_replace_map[i].function = rl_vi_overstrike; |
850 | |
851 | vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete; |
852 | vi_replace_map[ESC].function = rl_vi_movement_mode; |
853 | vi_replace_map[RETURN].function = rl_newline; |
854 | vi_replace_map[NEWLINE].function = rl_newline; |
855 | keymap = vi_replace_map; |
856 | } |