1 /* vi_mode.c -- A vi emulation mode for Bash.
3 Derived from code written by Jeff Sparkes (jeff1@????).
7 /* **************************************************************** */
9 /* VI Emulation Mode */
11 /* **************************************************************** */
13 /* Last string searched for from `/' or `?'. */
14 static char *vi_last_search = (char *)NULL;
15 static int vi_histpos;
17 /* Non-zero means enter insertion mode. */
18 int vi_doing_insert = 0;
21 /* Command keys which do movement for xxx_to commands. */
22 static char *vi_motion = " hl^$0ftFt;,%wbeWBE|";
24 /* Keymap used for vi replace characters. Created dynamically since
26 static Keymap vi_replace_map = (Keymap)NULL;
28 /* The number of characters inserted in the last replace operation. */
29 static vi_replace_count = 0;
31 /* Yank the nth arg from the previous line into this line at point. */
32 rl_vi_yank_arg (count)
35 rl_yank_nth_arg (count, 0);
38 /* Search again for the last thing searched for. */
39 rl_vi_search_again (ignore, key)
45 rl_vi_dosearch (vi_last_search, -1);
49 rl_vi_dosearch (vi_last_search, 1);
54 /* Do a vi style search. */
55 rl_vi_search (count, key)
76 vi_histpos = where_history ();
80 /* Reuse the line input buffer to read the search string. */
82 rl_end = rl_point = 0;
83 p = (char *)alloca (2 + (rl_prompt ? strlen (rl_prompt) : 0));
85 sprintf (p, "%s%c", rl_prompt ? rl_prompt : "", key);
89 while (c = rl_read_key ())
105 rl_dispatch (c, keymap);
115 maybe_unsave_line ();
129 free (vi_last_search);
131 vi_last_search = savestring (the_line);
132 rl_vi_dosearch (the_line, dir);
135 rl_vi_dosearch (string, dir)
139 int old, save = vi_histpos;
142 if (string == 0 || *string == 0 || vi_histpos < 0)
148 if ((save = history_search_pos (string, dir, vi_histpos + dir)) == -1)
150 maybe_unsave_line ();
159 old = where_history ();
160 history_set_pos (vi_histpos);
161 h = current_history ();
162 history_set_pos (old);
164 strcpy (the_line, h->line);
165 rl_undo_list = (UNDO_LIST *)h->data;
166 rl_end = strlen (the_line);
171 /* Completion, from vi's point of view. */
172 rl_vi_complete (ignore, key)
175 if (!whitespace (the_line[rl_point]))
177 if (!whitespace (the_line[rl_point + 1]))
178 rl_vi_end_word (1, 'E');
183 rl_complete_internal ('*');
185 rl_complete (0, key);
187 rl_vi_insertion_mode ();
190 /* Previous word in vi mode. */
191 rl_vi_prev_word (count, key)
196 rl_vi_next_word (-count, key);
200 if (uppercase_p (key))
206 /* Next word in vi mode. */
207 rl_vi_next_word (count, key)
212 rl_vi_prev_word (-count, key);
216 if (uppercase_p (key))
222 /* Move to the end of the ?next? word. */
223 rl_vi_end_word (count, key)
232 if (uppercase_p (key))
238 /* Move forward a word the way that 'W' does. */
242 while (count-- && rl_point < (rl_end - 1))
244 /* Skip until whitespace. */
245 while (!whitespace (the_line[rl_point]) && rl_point < rl_end)
248 /* Now skip whitespace. */
249 while (whitespace (the_line[rl_point]) && rl_point < rl_end)
257 while (count-- && rl_point > 0)
259 while (rl_point-- >= 0 && whitespace (the_line[rl_point]));
260 while (rl_point >= 0 && !whitespace (the_line[rl_point]))
269 while (count -- && rl_point < (rl_end - 1))
271 while (rl_point++ < rl_end && whitespace (the_line[rl_point]));
272 while (rl_point++ < rl_end && !whitespace (the_line[rl_point]));
280 while (count -- && rl_point < (rl_end - 1))
282 if (isident (the_line[rl_point]))
284 while (isident (the_line[rl_point]) && rl_point < rl_end)
287 else if (!whitespace (the_line[rl_point]))
289 while (!isident (the_line[rl_point]) &&
290 !whitespace (the_line[rl_point]) && rl_point < rl_end)
294 while (whitespace (the_line[rl_point]) && rl_point < rl_end)
302 while (count -- && rl_point > 0)
304 while (--rl_point > 0 && whitespace (the_line[rl_point]));
307 if (isident (the_line[rl_point]))
308 while (--rl_point >= 0 && isident (the_line[rl_point]));
310 while (--rl_point >= 0 && !isident (the_line[rl_point]) &&
311 !whitespace (the_line[rl_point]));
320 while (count -- && rl_point < rl_end - 1)
322 while (++rl_point < rl_end && whitespace (the_line[rl_point]));
324 if (rl_point < rl_end)
326 if (isident (the_line[rl_point]))
327 while (++rl_point < rl_end && isident (the_line[rl_point]));
329 while (++rl_point < rl_end && !isident (the_line[rl_point])
330 && !whitespace (the_line[rl_point]));
339 rl_vi_insertion_mode ();
345 if (rl_point < rl_end)
347 rl_vi_insertion_mode ();
354 rl_vi_append_mode ();
358 /* What to do in the case of C-d. */
359 rl_vi_eof_maybe (count, c)
362 rl_newline (1, '\n');
365 /* Insertion mode stuff. */
367 /* Switching from one mode to the other really just involves
368 switching keymaps. */
369 rl_vi_insertion_mode ()
371 keymap = vi_insertion_keymap;
374 rl_vi_movement_mode ()
379 keymap = vi_movement_keymap;
380 vi_done_inserting ();
387 rl_end_undo_group ();
392 rl_vi_arg_digit (count, c)
395 if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
398 rl_digit_argument (count, c);
401 /* Doesn't take an arg count in vi */
402 rl_vi_change_case (ignore1, ignore2)
403 int ignore1, ignore2;
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]);
412 /* Vi is kind of strange here. */
415 rl_begin_undo_group ();
418 rl_end_undo_group ();
425 rl_vi_put (count, key)
428 if (!uppercase_p (key))
437 if (rl_point && rl_point == rl_end)
446 rl_point = count - 1;
450 rl_vi_domove (key, nextkey)
459 if (!member (c, vi_motion))
463 save = rl_numeric_arg;
465 rl_numeric_arg *= save;
467 else if ((key == 'd' && c == 'd') ||
468 (key == 'c' && c == 'c'))
478 rl_dispatch (c, keymap);
480 /* No change in position means the command failed. */
481 if (rl_mark == rl_point)
484 if ((c == 'w' || c == 'W') && rl_point < rl_end)
487 if (rl_mark < rl_point)
488 exchange (rl_point, rl_mark);
493 /* A simplified loop for vi. Don't dispatch key at end.
494 Don't recognize minus sign? */
501 rl_message ("(arg: %d) ", arg_sign * rl_numeric_arg, 0);
502 key = c = rl_read_key ();
504 if (keymap[c].type == ISFUNC &&
505 keymap[c].function == rl_universal_argument)
514 rl_numeric_arg = (rl_numeric_arg * 10) + (c - '0');
516 rl_numeric_arg = (c - '0');
527 rl_vi_delete_to (count, key)
532 if (uppercase_p (key))
535 if (rl_vi_domove (key, &c))
541 if ((c != '|') && (c != 'h') && rl_mark < rl_end)
544 rl_kill_text (rl_point, rl_mark);
547 rl_vi_change_to (count, key)
552 if (uppercase_p (key))
555 if (rl_vi_domove (key, &c))
561 if ((c != '|') && (c != 'h') && rl_mark < rl_end)
564 rl_begin_undo_group ();
566 rl_kill_text (rl_point, rl_mark);
567 rl_vi_insertion_mode ();
570 rl_vi_yank_to (count, key)
573 int c, save = rl_point;
575 if (uppercase_p (key))
578 if (rl_vi_domove (key, &c))
584 rl_begin_undo_group ();
585 rl_kill_text (rl_point, rl_mark);
586 rl_end_undo_group ();
593 if (rl_point >= rl_end - 1)
595 rl_delete (count, 0);
600 rl_delete (count, 0);
603 /* Turn the current line into a comment in shell history. A ksh function */
607 rl_insert_text (": "); /* # doesn't work in interactive mode */
609 rl_newline (1, '\010');
614 rl_back_to_indent ();
617 rl_back_to_indent (ignore1, ignore2)
618 int ignore1, ignore2;
621 while (rl_point < rl_end && whitespace (the_line[rl_point]))
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 */
631 rl_vi_char_search (count, key)
635 static int orig_dir, dir;
638 if (key == ';' || key == ',')
639 dir = (key == ';' ? orig_dir : -orig_dir);
642 target = rl_getc (in_stream);
647 orig_dir = dir = FTO;
651 orig_dir = dir = BTO;
655 orig_dir = dir = FFIND;
659 orig_dir = dir = BFIND;
671 if (the_line[pos] == target)
693 if (the_line[pos] == target)
702 while (++pos < rl_end);
704 if (pos >= (rl_end - 1))
712 int count = 1, brack, pos;
715 if ((brack = rl_vi_bracktype (the_line[rl_point])) == 0)
717 while ((brack = rl_vi_bracktype (the_line[rl_point])) == 0 &&
718 rl_point < rl_end - 1)
737 int b = rl_vi_bracktype (the_line[pos]);
756 int b = rl_vi_bracktype (the_line[pos]);
792 c = rl_getc (in_stream);
801 rl_begin_undo_group ();
804 rl_end_undo_group ();
809 rl_vi_subst (count, key)
812 rl_begin_undo_group ();
815 if (uppercase_p (key))
823 rl_vi_insertion_mode ();
826 rl_vi_overstrike (count, key)
831 if (vi_doing_insert == 0)
834 rl_begin_undo_group ();
837 for (i = 0; i < count; i++)
840 rl_begin_undo_group ();
842 if (rl_point < rl_end)
850 rl_end_undo_group ();
854 rl_vi_overstrike_delete (count)
859 for (i = 0; i < count; i++)
861 if (vi_replace_count == 0)
875 if (vi_replace_count == 0 && vi_doing_insert)
877 rl_end_undo_group ();
887 vi_replace_count = 0;
889 vi_replace_map = rl_make_bare_keymap ();
891 for (i = ' '; i < 127; i++)
892 vi_replace_map[i].function = rl_vi_overstrike;
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;
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 ;.
906 rl_vi_possible_completions()
908 int save_pos = rl_point;
910 if (!index (" ;", the_line[rl_point]))
912 while (!index(" ;", the_line[++rl_point]))
915 else if (the_line[rl_point-1] == ';')
921 rl_possible_completions ();