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