commit eb108528044d310c994b967a64375c1b5c4ee770
parent ee84518b94a4159c230ff11436f465bcbedd3c06
Author: glepnir <glephunter@gmail.com>
Date: Tue, 3 Jun 2025 14:48:53 +0800
vim-patch:9.1.1426: completion: register contents not completed
Problem: CTRL-X CTRL-R only completes individual words from registers,
making it difficult to insert complete register content.
Solution: Add consecutive CTRL-X CTRL-R support - first press completes
words, second press completes full register lines, similar to
CTRL-X CTRL-L and CTRL-X CTRL-P behavior (glepnir).
closes: vim/vim#17395
https://github.com/vim/vim/commit/d5fdfa5c9cf00790cf720e15c580a591a09fa906
Co-authored-by: glepnir <glephunter@gmail.com>
Diffstat:
6 files changed, 79 insertions(+), 31 deletions(-)
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
@@ -146,7 +146,7 @@ commands in CTRL-X submode *i_CTRL-X_index*
|i_CTRL-X_CTRL-N| CTRL-X CTRL-N next completion
|i_CTRL-X_CTRL-O| CTRL-X CTRL-O omni completion
|i_CTRL-X_CTRL-P| CTRL-X CTRL-P previous completion
-|i_CTRL-X_CTRL-R| CTRL-X CTRL-R complete words from registers
+|i_CTRL-X_CTRL-R| CTRL-X CTRL-R complete contents from registers
|i_CTRL-X_CTRL-S| CTRL-X CTRL-S spelling suggestions
|i_CTRL-X_CTRL-T| CTRL-X CTRL-T complete identifiers from thesaurus
|i_CTRL-X_CTRL-Y| CTRL-X CTRL-Y scroll down
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
@@ -629,7 +629,7 @@ Completion can be done for:
11. omni completion |i_CTRL-X_CTRL-O|
12. Spelling suggestions |i_CTRL-X_s|
13. keywords in 'complete' |i_CTRL-N| |i_CTRL-P|
-14. words from registers |i_CTRL-X_CTRL-R|
+14. contents from registers |i_CTRL-X_CTRL-R|
Additionally, |i_CTRL-X_CTRL-Z| stops completion without changing the text.
@@ -1000,7 +1000,7 @@ CTRL-X CTRL-V Guess what kind of item is in front of the cursor and
completion, for example: >
:imap <Tab> <C-X><C-V>
-Completing words from registers *compl-register-words*
+Completing contents from registers *compl-register-words*
*i_CTRL-X_CTRL-R*
CTRL-X CTRL-R Guess what kind of item is in front of the cursor from
all registers and find the first match for it.
@@ -1014,6 +1014,11 @@ CTRL-X CTRL-R Guess what kind of item is in front of the cursor from
CTRL-P Search backwards for previous match. This match
replaces the previous one.
+ CTRL-X CTRL-R Further use of CTRL-X CTRL-R will copy the line
+ following the previous expansion in other contexts
+ unless a double CTRL-X is used (e.g. this switches
+ from completing register words to register contents).
+
User defined completion *compl-function*
Completion is done by a function that can be defined by the user with the
diff --git a/runtime/doc/usr_24.txt b/runtime/doc/usr_24.txt
@@ -187,7 +187,7 @@ with a certain type of item:
CTRL-X CTRL-D macro definitions (also in included files)
CTRL-X CTRL-I current and included files
CTRL-X CTRL-K words from a dictionary
- CTRL-X CTRL-R words from registers
+ CTRL-X CTRL-R contents from registers
CTRL-X CTRL-T words from a thesaurus
CTRL-X CTRL-] tags
CTRL-X CTRL-V Vim command line
diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt
@@ -230,7 +230,7 @@ Insert-mode completion. |ins-completion|
|i_CTRL-X_CTRL-D| definitions or macros
|i_CTRL-X_CTRL-O| Omni completion: clever completion
specifically for a file type
- |i_CTRL-X_CTRL-R| words from registers
+ |i_CTRL-X_CTRL-R| contents from registers
etc.
Long line support. |'wrap'| |'linebreak'|
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
@@ -4209,34 +4209,58 @@ static void get_register_completion(void)
continue;
}
- for (size_t j = 0; j < reg->y_size; j++) {
- char *str = reg->y_array[j].data;
- if (str == NULL) {
- continue;
- }
+ if (compl_status_adding()) {
+ for (size_t j = 0; j < reg->y_size; j++) {
+ char *str = reg->y_array[j].data;
+ if (str == NULL) {
+ continue;
+ }
- char *p = str;
- while (*p != NUL) {
- p = find_word_start(p);
- if (*p == NUL) {
- break;
+ int str_len = (int)strlen(str);
+ if (str_len == 0) {
+ continue;
}
- char *word_end = find_word_end(p);
- int len = (int)(word_end - p);
-
- // Add the word to the completion list
- if (len > 0 && (!compl_orig_text.data
- || (p_ic ? STRNICMP(p, compl_orig_text.data,
- compl_orig_text.size) == 0
- : strncmp(p, compl_orig_text.data,
- compl_orig_text.size) == 0))) {
- if (ins_compl_add_infercase(p, len, p_ic, NULL,
- dir, false, 0) == OK) {
+ if (!compl_orig_text.data
+ || (p_ic ? STRNICMP(str, compl_orig_text.data,
+ compl_orig_text.size) == 0
+ : strncmp(str, compl_orig_text.data,
+ compl_orig_text.size) == 0)) {
+ if (ins_compl_add_infercase(str, str_len, p_ic, NULL, dir, false, 0) == OK) {
dir = FORWARD;
}
}
- p = word_end;
+ }
+ } else {
+ for (size_t j = 0; j < reg->y_size; j++) {
+ char *str = reg->y_array[j].data;
+ if (str == NULL) {
+ continue;
+ }
+
+ char *p = str;
+ while (*p != NUL) {
+ p = find_word_start(p);
+ if (*p == NUL) {
+ break;
+ }
+
+ char *word_end = find_word_end(p);
+ int len = (int)(word_end - p);
+
+ // Add the word to the completion list
+ if (len > 0 && (!compl_orig_text.data
+ || (p_ic ? STRNICMP(p, compl_orig_text.data,
+ compl_orig_text.size) == 0
+ : strncmp(p, compl_orig_text.data,
+ compl_orig_text.size) == 0))) {
+ if (ins_compl_add_infercase(p, len, p_ic, NULL,
+ dir, false, 0) == OK) {
+ dir = FORWARD;
+ }
+ }
+ p = word_end;
+ }
}
}
@@ -5468,7 +5492,7 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col)
/// @return OK on success.
static int compl_get_info(char *line, int startcol, colnr_T curs_col, bool *line_invalid)
{
- if (ctrl_x_mode_normal()
+ if (ctrl_x_mode_normal() || ctrl_x_mode_register()
|| ((ctrl_x_mode & CTRL_X_WANT_IDENT)
&& !thesaurus_func_complete(ctrl_x_mode))) {
return get_normal_compl_info(line, startcol, curs_col);
@@ -5489,8 +5513,6 @@ static int compl_get_info(char *line, int startcol, colnr_T curs_col, bool *line
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()");
return FAIL;
@@ -5546,7 +5568,7 @@ static void ins_compl_continue_search(char *line)
if (compl_length < 1) {
compl_cont_status &= CONT_LOCAL;
}
- } else if (ctrl_x_mode_line_or_eval()) {
+ } else if (ctrl_x_mode_line_or_eval() || ctrl_x_mode_register()) {
compl_cont_status = CONT_ADDING | CONT_N_ADDS;
} else {
compl_cont_status = 0;
diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim
@@ -4751,6 +4751,27 @@ func Test_register_completion()
call feedkeys("Sze\<C-X>\<C-R>\<C-R>=string(complete_info(['mode']))\<CR>\<ESC>", "tx")
call assert_equal("zero{'mode': 'register'}", getline(1))
+ " Test consecutive CTRL-X CTRL-R (adding mode)Add commentMore actions
+ " First CTRL-X CTRL-R should split into words, second should use full content
+ let @f = "hello world test complete"
+ call setline(1, "hel")
+ call cursor(1, 3)
+ call feedkeys("a\<C-X>\<C-R>\<C-N>\<Esc>", 'tx')
+ call assert_equal("hello", getline(1))
+
+ " Second consecutive CTRL-X CTRL-R should complete with full content
+ call setline(1, "hello")
+ call cursor(1, 5)
+ call feedkeys("a\<C-X>\<C-R>\<C-X>\<C-R>\<Esc>", 'tx')
+ call assert_equal("hello world test complete", getline(1))
+
+ " Test consecutive completion with multi-line register
+ let @g = "first line content\nsecond line here\nthird line data"
+ call setline(1, "first")
+ call cursor(1, 5)
+ call feedkeys("a\<C-X>\<C-R>\<C-X>\<C-R>\<Esc>", 'tx')
+ call assert_equal("first line content", getline(1))
+
" Clean up
bwipe!
delfunc GetItems