From d5fdfa5c9cf00790cf720e15c580a591a09fa906 Mon Sep 17 00:00:00 2001 From: glepnir Date: Mon, 2 Jun 2025 19:45:41 +0200 Subject: [PATCH] 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: #17395 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- runtime/doc/index.txt | 4 +- runtime/doc/insert.txt | 11 +++- runtime/doc/usr_24.txt | 4 +- runtime/doc/vi_diff.txt | 4 +- src/insexpand.c | 98 ++++++++++++++++++++++--------- src/testdir/test_ins_complete.vim | 21 +++++++ src/version.c | 2 + 7 files changed, 108 insertions(+), 36 deletions(-) diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index d03d81effa..ff27994706 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1,4 +1,4 @@ -*index.txt* For Vim version 9.1. Last change: 2025 May 26 +*index.txt* For Vim version 9.1. Last change: 2025 Jun 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -163,7 +163,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 index 553183d74d..9ec46825e4 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1,4 +1,4 @@ -*insert.txt* For Vim version 9.1. Last change: 2025 May 26 +*insert.txt* For Vim version 9.1. Last change: 2025 Jun 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -649,7 +649,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. @@ -1021,7 +1021,7 @@ CTRL-X CTRL-V Guess what kind of item is in front of the cursor and :imap -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. @@ -1035,6 +1035,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 index 250bd17ea6..0f72495747 100644 --- a/runtime/doc/usr_24.txt +++ b/runtime/doc/usr_24.txt @@ -1,4 +1,4 @@ -*usr_24.txt* For Vim version 9.1. Last change: 2018 Mar 18 +*usr_24.txt* For Vim version 9.1. Last change: 2025 Jun 02 VIM USER MANUAL - by Bram Moolenaar @@ -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 index ae14968bbe..b35fe77c78 100644 --- a/runtime/doc/vi_diff.txt +++ b/runtime/doc/vi_diff.txt @@ -1,4 +1,4 @@ -*vi_diff.txt* For Vim version 9.1. Last change: 2025 Mar 28 +*vi_diff.txt* For Vim version 9.1. Last change: 2025 Jun 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -338,7 +338,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/insexpand.c b/src/insexpand.c index 51dd675b13..9b5eec500d 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -4935,35 +4935,83 @@ get_register_completion(void) reg = (yankreg_T *)reg_ptr; - for (int j = 0; j < reg->y_size; j++) + if (compl_status_adding()) { - char_u *str = reg->y_array[j].string; - if (str == NULL) - continue; - - char_u *p = str; - while (*p != NUL) + for (int j = 0; j < reg->y_size; j++) { - p = find_word_start(p); - if (*p == NUL) - break; + char_u *str = reg->y_array[j].string; + if (str == NULL) + continue; - char_u *word_end = find_word_end(p); + int str_len = (int)STRLEN(str); + if (str_len == 0) + continue; - // 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 (!compl_orig_text.string + || (p_ic ? STRNICMP(str, compl_orig_text.string, + compl_orig_text.length) == 0 + : STRNCMP(str, compl_orig_text.string, + compl_orig_text.length) == 0)) { - if (ins_compl_add_infercase(p, len, p_ic, NULL, - dir, FALSE, 0) == OK) + if (ins_compl_add_infercase(str, str_len, p_ic, NULL, dir, FALSE, 0) == OK) dir = FORWARD; } + } + } + else + { + for (int j = 0; j < reg->y_size; j++) + { + char_u *str = reg->y_array[j].string; + if (str == NULL) + continue; - p = word_end; + // Calculate the safe end of string to avoid null byte issues + char_u *str_end = str + STRLEN(str); + char_u *p = str; + + // Safely iterate through the string + while (p < str_end && *p != NUL) + { + char_u *old_p = p; + p = find_word_start(p); + if (p >= str_end || *p == NUL) + break; + + char_u *word_end = find_word_end(p); + + if (word_end <= p) + { + if (has_mbyte) + word_end = p + (*mb_ptr2len)(p); + else + word_end = p + 1; + } + + if (word_end > str_end) + word_end = str_end; + + 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; + + if (p <= old_p) + { + p = old_p + 1; + if (has_mbyte && p < str_end) + p = old_p + (*mb_ptr2len)(old_p); + } + } } } @@ -6379,7 +6427,7 @@ get_spell_compl_info(int startcol UNUSED, colnr_T curs_col UNUSED) static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, int *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))) { @@ -6410,10 +6458,6 @@ compl_get_info(char_u *line, int startcol, colnr_T curs_col, int *line_invalid) 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()"); @@ -6480,7 +6524,7 @@ ins_compl_continue_search(char_u *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/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index f3c29d1e76..7d67e9f057 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -4670,6 +4670,27 @@ func Test_register_completion() call feedkeys("Sze\\\=string(complete_info(['mode']))\\", "tx") call assert_equal("zero{'mode': 'register'}", getline(1)) + " Test consecutive CTRL-X CTRL-R (adding mode) + " 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\\\\", '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\\\\\", '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\\\\\", 'tx') + call assert_equal("first line content", getline(1)) + " Clean up bwipe! delfunc GetItems diff --git a/src/version.c b/src/version.c index df6eb585ce..3160bdfeca 100644 --- a/src/version.c +++ b/src/version.c @@ -709,6 +709,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1426, /**/ 1425, /**/