summaryrefslogtreecommitdiff
path: root/readline/vi_mode.c
diff options
context:
space:
mode:
Diffstat (limited to 'readline/vi_mode.c')
-rw-r--r--readline/vi_mode.c291
1 files changed, 248 insertions, 43 deletions
diff --git a/readline/vi_mode.c b/readline/vi_mode.c
index a3c35786c36..56d2e72fb7f 100644
--- a/readline/vi_mode.c
+++ b/readline/vi_mode.c
@@ -1,7 +1,7 @@
/* vi_mode.c -- A vi emulation mode for Bash.
Derived from code written by Jeff Sparkes (jsparkes@bnr.ca). */
-/* Copyright (C) 1987-2010 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2016 Free Software Foundation, Inc.
This file is part of the GNU Readline Library (Readline), a library
for reading lines of text with interactive input and history editing.
@@ -67,6 +67,9 @@ int _rl_vi_last_command = 'i'; /* default `.' puts you in insert mode */
_rl_vimotion_cxt *_rl_vimvcxt = 0;
+/* Non-zero indicates we are redoing a vi-mode command with `.' */
+int _rl_vi_redoing;
+
/* Non-zero means enter insertion mode. */
static int _rl_vi_doing_insert;
@@ -100,17 +103,19 @@ static int _rl_vi_last_replacement;
static int _rl_vi_last_key_before_insert;
-static int vi_redoing;
-
/* Text modification commands. These are the `redoable' commands. */
static const char * const vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
/* Arrays for the saved marks. */
static int vi_mark_chars['z' - 'a' + 1];
+static void _rl_vi_replace_insert PARAMS((int));
+static void _rl_vi_save_replace PARAMS((void));
static void _rl_vi_stuff_insert PARAMS((int));
static void _rl_vi_save_insert PARAMS((UNDO_LIST *));
+static void vi_save_insert_buffer PARAMS ((int, int));
+
static void _rl_vi_backup PARAMS((void));
static int _rl_vi_arg_dispatch PARAMS((int));
@@ -188,6 +193,29 @@ _rl_vi_textmod_command (c)
return (member (c, vi_textmod));
}
+int
+_rl_vi_motion_command (c)
+ int c;
+{
+ return (member (c, vi_motion));
+}
+
+static void
+_rl_vi_replace_insert (count)
+ int count;
+{
+ int nchars;
+
+ nchars = strlen (vi_insert_buffer);
+
+ rl_begin_undo_group ();
+ while (count--)
+ /* nchars-1 to compensate for _rl_replace_text using `end+1' in call
+ to rl_delete_text */
+ _rl_replace_text (vi_insert_buffer, rl_point, rl_point+nchars-1);
+ rl_end_undo_group ();
+}
+
static void
_rl_vi_stuff_insert (count)
int count;
@@ -207,14 +235,14 @@ rl_vi_redo (count, c)
{
int r;
- if (!rl_explicit_arg)
+ if (rl_explicit_arg == 0)
{
rl_numeric_arg = _rl_vi_last_repeat;
rl_arg_sign = _rl_vi_last_arg_sign;
}
r = 0;
- vi_redoing = 1;
+ _rl_vi_redoing = 1;
/* If we're redoing an insert with `i', stuff in the inserted text
and do not go into insertion mode. */
if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
@@ -224,6 +252,13 @@ rl_vi_redo (count, c)
if (rl_point > 0)
_rl_vi_backup ();
}
+ else if (_rl_vi_last_command == 'R' && vi_insert_buffer && *vi_insert_buffer)
+ {
+ _rl_vi_replace_insert (count);
+ /* And back up point over the last character inserted. */
+ if (rl_point > 0)
+ _rl_vi_backup ();
+ }
/* Ditto for redoing an insert with `I', but move to the beginning of the
line like the `I' command does. */
else if (_rl_vi_last_command == 'I' && vi_insert_buffer && *vi_insert_buffer)
@@ -253,7 +288,8 @@ rl_vi_redo (count, c)
}
else
r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
- vi_redoing = 0;
+
+ _rl_vi_redoing = 0;
return (r);
}
@@ -437,7 +473,7 @@ rl_vi_end_word (count, key)
if (count < 0)
{
rl_ding ();
- return -1;
+ return 1;
}
if (_rl_uppercase_p (key))
@@ -679,6 +715,8 @@ rl_vi_insertion_mode (count, key)
{
_rl_keymap = vi_insertion_keymap;
_rl_vi_last_key_before_insert = key;
+ if (_rl_show_mode_in_prompt)
+ _rl_reset_prompt ();
return (0);
}
@@ -691,6 +729,43 @@ rl_vi_insert_mode (count, key)
}
static void
+vi_save_insert_buffer (start, len)
+ int start, len;
+{
+ /* Same code as _rl_vi_save_insert below */
+ if (len >= vi_insert_buffer_size)
+ {
+ vi_insert_buffer_size += (len + 32) - (len % 32);
+ vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
+ }
+ strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
+ vi_insert_buffer[len-1] = '\0';
+}
+
+static void
+_rl_vi_save_replace ()
+{
+ int len, start, end;
+ UNDO_LIST *up;
+
+ up = rl_undo_list;
+ if (up == 0 || up->what != UNDO_END || vi_replace_count <= 0)
+ {
+ if (vi_insert_buffer_size >= 1)
+ vi_insert_buffer[0] = '\0';
+ return;
+ }
+ /* Let's try it the quick and easy way for now. This should essentially
+ accommodate every UNDO_INSERT and save the inserted text to
+ vi_insert_buffer */
+ end = rl_point;
+ start = end - vi_replace_count + 1;
+ len = vi_replace_count + 1;
+
+ vi_save_insert_buffer (start, len);
+}
+
+static void
_rl_vi_save_insert (up)
UNDO_LIST *up;
{
@@ -706,13 +781,8 @@ _rl_vi_save_insert (up)
start = up->start;
end = up->end;
len = end - start + 1;
- if (len >= vi_insert_buffer_size)
- {
- vi_insert_buffer_size += (len + 32) - (len % 32);
- vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
- }
- strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
- vi_insert_buffer[len-1] = '\0';
+
+ vi_save_insert_buffer (start, len);
}
void
@@ -728,7 +798,10 @@ _rl_vi_done_inserting ()
on absolute indices into the line which may change (though they
probably will not). */
_rl_vi_doing_insert = 0;
- _rl_vi_save_insert (rl_undo_list->next);
+ if (_rl_vi_last_key_before_insert == 'R')
+ _rl_vi_save_replace (); /* Half the battle */
+ else
+ _rl_vi_save_insert (rl_undo_list->next);
vi_continued_command = 1;
}
else
@@ -762,6 +835,9 @@ rl_vi_movement_mode (count, key)
if (RL_ISSTATE (RL_STATE_VICMDONCE) == 0)
rl_free_undo_list ();
+ if (_rl_show_mode_in_prompt)
+ _rl_reset_prompt ();
+
RL_SETSTATE (RL_STATE_VICMDONCE);
return (0);
}
@@ -1022,28 +1098,55 @@ static int
rl_domove_motion_callback (m)
_rl_vimotion_cxt *m;
{
- int c, save, r;
- int old_end;
+ int c;
_rl_vi_last_motion = c = m->motion;
/* Append a blank character temporarily so that the motion routines
- work right at the end of the line. */
- old_end = rl_end;
+ work right at the end of the line. Original value of rl_end is saved
+ as m->end. */
rl_line_buffer[rl_end++] = ' ';
rl_line_buffer[rl_end] = '\0';
_rl_dispatch (c, _rl_keymap);
- /* Remove the blank that we added. */
- rl_end = old_end;
+#if defined (READLINE_CALLBACKS)
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ {
+ /* Messy case where char search can be vi motion command; see rest of
+ details in callback.c. vi_char_search and callback_char_search just
+ set and unset the CHARSEARCH state. This is where any vi motion
+ command that needs to set its own state should be handled, with any
+ corresponding code to manage that state in callback.c */
+ if (RL_ISSTATE (RL_STATE_CHARSEARCH))
+ return 0;
+ else
+ return (_rl_vi_domove_motion_cleanup (c, m));
+ }
+#endif
+
+ return (_rl_vi_domove_motion_cleanup (c, m));
+}
+
+int
+_rl_vi_domove_motion_cleanup (c, m)
+ int c;
+ _rl_vimotion_cxt *m;
+{
+ int r;
+
+ /* Remove the blank that we added in rl_domove_motion_callback. */
+ rl_end = m->end;
rl_line_buffer[rl_end] = '\0';
if (rl_point > rl_end)
rl_point = rl_end;
/* No change in position means the command failed. */
if (rl_mark == rl_point)
- return (-1);
+ {
+ RL_UNSETSTATE (RL_STATE_VIMOTION);
+ return (-1);
+ }
/* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
word. If we are not at the end of the line, and we are on a
@@ -1177,8 +1280,8 @@ _rl_vi_domove_callback (m)
int c, r;
m->motion = c = rl_vi_domove_getchar (m);
- /* XXX - what to do if this returns -1? Should we return 1 for eof to
- callback code? */
+ if (c < 0)
+ return 1; /* EOF */
r = rl_domove_read_callback (m);
return ((r == 0) ? r : 1); /* normalize return values */
@@ -1234,11 +1337,19 @@ rl_vi_delete_to (count, key)
_rl_vimvcxt->motion = '$';
r = rl_domove_motion_callback (_rl_vimvcxt);
}
- else if (vi_redoing)
+ else if (_rl_vi_redoing && _rl_vi_last_motion != 'd') /* `dd' is special */
{
_rl_vimvcxt->motion = _rl_vi_last_motion;
r = rl_domove_motion_callback (_rl_vimvcxt);
}
+ else if (_rl_vi_redoing) /* handle redoing `dd' here */
+ {
+ _rl_vimvcxt->motion = _rl_vi_last_motion;
+ rl_mark = rl_end;
+ rl_beg_of_line (1, key);
+ RL_UNSETSTATE (RL_STATE_VIMOTION);
+ r = vidomove_dispatch (_rl_vimvcxt);
+ }
#if defined (READLINE_CALLBACKS)
else if (RL_ISSTATE (RL_STATE_CALLBACK))
{
@@ -1276,7 +1387,7 @@ vi_change_dispatch (m)
if ((_rl_to_upper (m->motion) == 'W') && rl_point < m->start)
rl_point = m->start;
- if (vi_redoing)
+ if (_rl_vi_redoing)
{
if (vi_insert_buffer && *vi_insert_buffer)
rl_begin_undo_group ();
@@ -1316,11 +1427,19 @@ rl_vi_change_to (count, key)
_rl_vimvcxt->motion = '$';
r = rl_domove_motion_callback (_rl_vimvcxt);
}
- else if (vi_redoing)
+ else if (_rl_vi_redoing && _rl_vi_last_motion != 'c') /* `cc' is special */
{
_rl_vimvcxt->motion = _rl_vi_last_motion;
r = rl_domove_motion_callback (_rl_vimvcxt);
}
+ else if (_rl_vi_redoing) /* handle redoing `cc' here */
+ {
+ _rl_vimvcxt->motion = _rl_vi_last_motion;
+ rl_mark = rl_end;
+ rl_beg_of_line (1, key);
+ RL_UNSETSTATE (RL_STATE_VIMOTION);
+ r = vidomove_dispatch (_rl_vimvcxt);
+ }
#if defined (READLINE_CALLBACKS)
else if (RL_ISSTATE (RL_STATE_CALLBACK))
{
@@ -1377,6 +1496,19 @@ rl_vi_yank_to (count, key)
_rl_vimvcxt->motion = '$';
r = rl_domove_motion_callback (_rl_vimvcxt);
}
+ else if (_rl_vi_redoing && _rl_vi_last_motion != 'y') /* `yy' is special */
+ {
+ _rl_vimvcxt->motion = _rl_vi_last_motion;
+ r = rl_domove_motion_callback (_rl_vimvcxt);
+ }
+ else if (_rl_vi_redoing) /* handle redoing `yy' here */
+ {
+ _rl_vimvcxt->motion = _rl_vi_last_motion;
+ rl_mark = rl_end;
+ rl_beg_of_line (1, key);
+ RL_UNSETSTATE (RL_STATE_VIMOTION);
+ r = vidomove_dispatch (_rl_vimvcxt);
+ }
#if defined (READLINE_CALLBACKS)
else if (RL_ISSTATE (RL_STATE_CALLBACK))
{
@@ -1438,7 +1570,7 @@ rl_vi_rubout (count, key)
if (rl_point == 0)
{
rl_ding ();
- return -1;
+ return 1;
}
opoint = rl_point;
@@ -1469,7 +1601,7 @@ rl_vi_delete (count, key)
if (rl_end == 0)
{
rl_ding ();
- return -1;
+ return 1;
}
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
@@ -1488,6 +1620,62 @@ rl_vi_delete (count, key)
return (0);
}
+/* This does what Posix specifies vi-mode C-w to do: using whitespace and
+ punctuation characters as the word boundaries. */
+
+#define vi_unix_word_boundary(c) (whitespace(c) || ispunct(c))
+
+int
+rl_vi_unix_word_rubout (count, key)
+ int count, key;
+{
+ int orig_point;
+
+ if (rl_point == 0)
+ rl_ding ();
+ else
+ {
+ orig_point = rl_point;
+ if (count <= 0)
+ count = 1;
+
+ while (count--)
+ {
+ /* This isn't quite what ksh93 does but it seems to match what the
+ Posix description of sh specifies, with a few accommodations
+ for sequences of whitespace characters between words and at
+ the end of the line. */
+
+ /* Skip over whitespace at the end of the line as a special case */
+ if (rl_point > 0 && (rl_line_buffer[rl_point] == 0) &&
+ whitespace (rl_line_buffer[rl_point - 1]))
+ while (--rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
+ ;
+
+ /* If we're at the start of a word, move back to word boundary so we
+ move back to the `preceding' word */
+ if (rl_point > 0 && (vi_unix_word_boundary (rl_line_buffer[rl_point]) == 0) &&
+ vi_unix_word_boundary (rl_line_buffer[rl_point - 1]))
+ rl_point--;
+
+ /* If we are at a word boundary (whitespace/punct), move backward
+ past a sequence of word boundary characters. If we are at the
+ end of a word (non-word boundary), move back to a word boundary */
+ if (rl_point > 0 && vi_unix_word_boundary (rl_line_buffer[rl_point]))
+ while (rl_point && vi_unix_word_boundary (rl_line_buffer[rl_point - 1]))
+ rl_point--;
+ else if (rl_point > 0 && vi_unix_word_boundary (rl_line_buffer[rl_point]) == 0)
+ while (rl_point && (vi_unix_word_boundary (rl_line_buffer[rl_point - 1]) == 0))
+ rl_point--;
+ }
+
+ rl_kill_text (orig_point, rl_point);
+ }
+
+ return 0;
+}
+
+
int
rl_vi_back_to_indent (count, key)
int count, key;
@@ -1522,7 +1710,10 @@ _rl_vi_callback_char_search (data)
#endif
if (c <= 0)
- return -1;
+ {
+ RL_UNSETSTATE (RL_STATE_CHARSEARCH);
+ return -1;
+ }
#if !defined (HANDLE_MULTIBYTE)
_rl_vi_last_search_char = c;
@@ -1530,6 +1721,7 @@ _rl_vi_callback_char_search (data)
_rl_callback_func = 0;
_rl_want_redisplay = 1;
+ RL_UNSETSTATE (RL_STATE_CHARSEARCH);
#if defined (HANDLE_MULTIBYTE)
return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_mbchar, _rl_vi_last_search_mblen));
@@ -1554,13 +1746,13 @@ rl_vi_char_search (count, key)
if (key == ';' || key == ',')
{
if (_rl_cs_orig_dir == 0)
- return -1;
+ return 1;
#if defined (HANDLE_MULTIBYTE)
if (_rl_vi_last_search_mblen == 0)
- return -1;
+ return 1;
#else
if (_rl_vi_last_search_char == 0)
- return -1;
+ return 1;
#endif
_rl_cs_dir = (key == ';') ? _rl_cs_orig_dir : -_rl_cs_orig_dir;
}
@@ -1585,7 +1777,7 @@ rl_vi_char_search (count, key)
break;
}
- if (vi_redoing)
+ if (_rl_vi_redoing)
{
/* set target and tlen below */
}
@@ -1594,7 +1786,9 @@ rl_vi_char_search (count, key)
{
_rl_callback_data = _rl_callback_data_alloc (count);
_rl_callback_data->i1 = _rl_cs_dir;
+ _rl_callback_data->i2 = key;
_rl_callback_func = _rl_vi_callback_char_search;
+ RL_SETSTATE (RL_STATE_CHARSEARCH);
return (0);
}
#endif
@@ -1659,7 +1853,7 @@ rl_vi_match (ignore, key)
{
rl_point = pos;
rl_ding ();
- return -1;
+ return 1;
}
}
@@ -1689,7 +1883,7 @@ rl_vi_match (ignore, key)
else
{
rl_ding ();
- return -1;
+ return 1;
}
}
}
@@ -1713,7 +1907,7 @@ rl_vi_match (ignore, key)
else
{
rl_ding ();
- return -1;
+ return 1;
}
}
}
@@ -1819,7 +2013,7 @@ rl_vi_change_char (count, key)
int c;
char mb[MB_LEN_MAX];
- if (vi_redoing)
+ if (_rl_vi_redoing)
{
c = _rl_vi_last_replacement;
mb[0] = c;
@@ -1847,7 +2041,7 @@ rl_vi_subst (count, key)
int count, key;
{
/* If we are redoing, rl_vi_change_to will stuff the last motion char */
- if (vi_redoing == 0)
+ if (_rl_vi_redoing == 0)
rl_stuff_char ((key == 'S') ? 'c' : 'l'); /* `S' == `cc', `s' == `cl' */
return (rl_vi_change_to (count, 'c'));
@@ -1911,14 +2105,20 @@ rl_vi_replace (count, key)
vi_replace_count = 0;
- if (!vi_replace_map)
+ if (vi_replace_map == 0)
{
vi_replace_map = rl_make_bare_keymap ();
+ for (i = 0; i < ' '; i++)
+ if (vi_insertion_keymap[i].type == ISFUNC)
+ vi_replace_map[i].function = vi_insertion_keymap[i].function;
+
for (i = ' '; i < KEYMAP_SIZE; i++)
vi_replace_map[i].function = rl_vi_overstrike;
vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
+
+ /* Make sure these are what we want. */
vi_replace_map[ESC].function = rl_vi_movement_mode;
vi_replace_map[RETURN].function = rl_newline;
vi_replace_map[NEWLINE].function = rl_newline;
@@ -1931,7 +2131,12 @@ rl_vi_replace (count, key)
vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
}
+
+ rl_vi_start_inserting (key, 1, rl_arg_sign);
+
+ _rl_vi_last_key_before_insert = key;
_rl_keymap = vi_replace_map;
+
return (0);
}
@@ -1976,7 +2181,7 @@ _rl_vi_set_mark ()
if (ch < 0 || ch < 'a' || ch > 'z') /* make test against 0 explicit */
{
rl_ding ();
- return -1;
+ return 1;
}
ch -= 'a';
vi_mark_chars[ch] = rl_point;
@@ -2028,14 +2233,14 @@ _rl_vi_goto_mark ()
else if (ch < 0 || ch < 'a' || ch > 'z') /* make test against 0 explicit */
{
rl_ding ();
- return -1;
+ return 1;
}
ch -= 'a';
if (vi_mark_chars[ch] == -1)
{
rl_ding ();
- return -1;
+ return 1;
}
rl_point = vi_mark_chars[ch];
return 0;