patch 9.1.1408: not easily possible to complete from register content
Problem: not easily possible to complete from register content
Solution: add register-completion submode using i_CTRL-X_CTRL-R
(glepnir)
closes: #17354
Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/insexpand.c b/src/insexpand.c
index 76fb8f3..c7d6fd4 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -38,6 +38,7 @@
# define CTRL_X_LOCAL_MSG 15 // only used in "ctrl_x_msgs"
# define CTRL_X_EVAL 16 // for builtin function complete()
# define CTRL_X_CMDLINE_CTRL_X 17 // CTRL-X typed in CTRL_X_CMDLINE
+# define CTRL_X_REGISTER 18 // complete words from registers
# define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
@@ -45,7 +46,7 @@
static char *ctrl_x_msgs[] =
{
N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl.
- N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"),
+ N_(" ^X mode (^]^D^E^F^I^K^L^N^O^P^Rs^U^V^Y)"),
NULL, // CTRL_X_SCROLL: depends on state
N_(" Whole line completion (^L^N^P)"),
N_(" File name completion (^F^N^P)"),
@@ -62,6 +63,7 @@
N_(" Keyword Local completion (^N^P)"),
NULL, // CTRL_X_EVAL doesn't use msg.
N_(" Command-line completion (^V^N^P)"),
+ N_(" Register completion (^N^P)"),
};
#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
@@ -84,6 +86,7 @@
NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
"eval",
"cmdline",
+ "register",
};
#endif
@@ -330,6 +333,8 @@
{ return ctrl_x_mode == CTRL_X_EVAL; }
int ctrl_x_mode_line_or_eval(void)
{ return ctrl_x_mode == CTRL_X_WHOLE_LINE || ctrl_x_mode == CTRL_X_EVAL; }
+int ctrl_x_mode_register(void)
+ { return ctrl_x_mode == CTRL_X_REGISTER; }
/*
* Whether other than default completion has been selected.
@@ -460,7 +465,7 @@
vim_is_ctrl_x_key(int c)
{
// Always allow ^R - let its results then be checked
- if (c == Ctrl_R)
+ if (c == Ctrl_R && ctrl_x_mode != CTRL_X_REGISTER)
return TRUE;
// Accept <PageUp> and <PageDown> if the popup menu is visible.
@@ -479,7 +484,7 @@
|| c == Ctrl_N || c == Ctrl_T || c == Ctrl_V
|| c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O
|| c == Ctrl_S || c == Ctrl_K || c == 's'
- || c == Ctrl_Z);
+ || c == Ctrl_Z || c == Ctrl_R);
case CTRL_X_SCROLL:
return (c == Ctrl_Y || c == Ctrl_E);
case CTRL_X_WHOLE_LINE:
@@ -511,6 +516,8 @@
return (c == Ctrl_S || c == Ctrl_P || c == Ctrl_N);
case CTRL_X_EVAL:
return (c == Ctrl_P || c == Ctrl_N);
+ case CTRL_X_REGISTER:
+ return (c == Ctrl_R || c == Ctrl_P || c == Ctrl_N);
}
internal_error("vim_is_ctrl_x_key()");
return FALSE;
@@ -2535,7 +2542,7 @@
static int
set_ctrl_x_mode(int c)
{
- int retval = FALSE;
+ int retval = FALSE;
switch (c)
{
@@ -2563,8 +2570,11 @@
ctrl_x_mode = CTRL_X_DICTIONARY;
break;
case Ctrl_R:
- // Register insertion without exiting CTRL-X mode
- // Simply allow ^R to happen without affecting ^X mode
+ // When CTRL-R is followed by '=', don't trigger register completion
+ // This allows expressions like <C-R>=func()<CR> to work normally
+ if (vpeekc() == '=')
+ break;
+ ctrl_x_mode = CTRL_X_REGISTER;
break;
case Ctrl_T:
// complete words from a thesaurus
@@ -4784,6 +4794,83 @@
#endif
/*
+ * Get completion matches from register contents.
+ * Extracts words from all available registers and adds them to the completion list.
+ */
+ static void
+get_register_completion(void)
+{
+ int dir = compl_direction;
+ yankreg_T *reg = NULL;
+ void *reg_ptr = NULL;
+
+ for (int i = 0; i < NUM_REGISTERS; i++)
+ {
+ int regname = 0;
+
+ if (i == 0)
+ regname = '"'; // unnamed register
+ else if (i < 10)
+ regname = '0' + i;
+ else if (i == DELETION_REGISTER)
+ regname = '-';
+#ifdef FEAT_CLIPBOARD
+ else if (i == STAR_REGISTER)
+ regname = '*';
+ else if (i == PLUS_REGISTER)
+ regname = '+';
+#endif
+ else
+ regname = 'a' + i - 10;
+
+ // Skip invalid or black hole register
+ if (!valid_yank_reg(regname, FALSE) || regname == '_')
+ continue;
+
+ reg_ptr = get_register(regname, FALSE);
+ if (reg_ptr == NULL)
+ continue;
+
+ reg = (yankreg_T *)reg_ptr;
+
+ for (int j = 0; j < reg->y_size; j++)
+ {
+ char_u *str = reg->y_array[j].string;
+ if (str == NULL)
+ continue;
+
+ char_u *p = str;
+ while (*p != NUL)
+ {
+ p = find_word_start(p);
+ if (*p == NUL)
+ break;
+
+ char_u *word_end = find_word_end(p);
+
+ // Add the word to the completion list
+ int len = (int)(word_end - p);
+ if (len > 0 && (!compl_orig_text.string
+ || (p_ic ? STRNICMP(p, compl_orig_text.string,
+ compl_orig_text.length) == 0
+ : STRNCMP(p, compl_orig_text.string,
+ compl_orig_text.length) == 0)))
+ {
+ if (ins_compl_add_infercase(p, len, p_ic, NULL,
+ dir, FALSE, 0) == OK)
+ dir = FORWARD;
+ }
+
+ p = word_end;
+ }
+ }
+
+ // Free the register copy
+ put_register(regname, reg_ptr);
+ }
+}
+
+/*
* get the next set of completion matches for "type".
* Returns TRUE if a new match is found. Otherwise returns FALSE.
*/
@@ -4838,6 +4925,10 @@
get_next_spell_completion(st->first_match_pos.lnum);
break;
+ case CTRL_X_REGISTER:
+ get_register_completion();
+ break;
+
default: // normal ^P/^N and ^X^L
found_new_match = get_next_default_completion(st, ini);
if (found_new_match == FAIL && st->ins_buf == curbuf)
@@ -6125,6 +6216,10 @@
return FAIL;
*line_invalid = TRUE; // "line" may have become invalid
}
+ else if (ctrl_x_mode_register())
+ {
+ return get_normal_compl_info(line, startcol, curs_col);
+ }
else
{
internal_error("ins_complete()");