]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1408: not easily possible to complete from register content v9.1.1408
authorglepnir <glephunter@gmail.com>
Mon, 26 May 2025 16:23:27 +0000 (18:23 +0200)
committerChristian Brabandt <cb@256bit.org>
Mon, 26 May 2025 16:25:57 +0000 (18:25 +0200)
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>
13 files changed:
runtime/doc/index.txt
runtime/doc/insert.txt
runtime/doc/options.txt
runtime/doc/tags
runtime/doc/todo.txt
runtime/doc/usr_24.txt
runtime/doc/version9.txt
runtime/doc/vi_diff.txt
src/edit.c
src/insexpand.c
src/proto/insexpand.pro
src/testdir/test_ins_complete.vim
src/version.c

index 00a09ae145e1fcb9bf8787e759d20ff8b5c54591..d03d81effac88755b6c53b99bebbd6443f3b9201 100644 (file)
@@ -1,4 +1,4 @@
-*index.txt*     For Vim version 9.1.  Last change: 2025 May 14
+*index.txt*     For Vim version 9.1.  Last change: 2025 May 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -163,6 +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-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
index f3d92b290b7815c52cfb876cae48672f68c9a726..553183d74dad58d7e8a44f17132f8d3de885d3f7 100644 (file)
@@ -1,4 +1,4 @@
-*insert.txt*    For Vim version 9.1.  Last change: 2025 May 08
+*insert.txt*    For Vim version 9.1.  Last change: 2025 May 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -649,6 +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|
 
 Additionally, |i_CTRL-X_CTRL-Z| stops completion without changing the text.
 
@@ -1019,6 +1020,21 @@ 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*
+                                                       *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.
+                       Further use of CTRL-R (without CTRL-X) will insert the
+                       register content, see |i_CTRL-R|.
+                       'ignorecase' applies to the matching.
+
+       CTRL-N          Search forwards for next match.  This match replaces
+                       the previous one.
+
+       CTRL-P          Search backwards for previous match.  This match
+                       replaces the previous one.
+
 User defined completion                                        *compl-function*
 
 Completion is done by a function that can be defined by the user with the
index e74c5e8a5945ef36bbb7511a3b7fc22cab2aa1f3..f0a7e9eefc338c05bc7f68987752326ac0118056 100644 (file)
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.1.  Last change: 2025 May 16
+*options.txt*  For Vim version 9.1.  Last change: 2025 May 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -4658,7 +4658,8 @@ A jump table for the options with a short description can be found at |Q_op|.
 'ignorecase' 'ic'      boolean (default off)
                        global
        Ignore case in search patterns, |cmdline-completion|, when
-       searching in the tags file, and non-|Vim9| |expr-==|.
+       searching in the tags file, non-|Vim9| |expr-==| and for Insert-mode
+       completion |ins-completion|.
        Also see 'smartcase' and 'tagcase'.
        Can be overruled by using "\c" or "\C" in the pattern, see
        |/ignorecase|.
index e58621298a14399a612cc6bbcd4bbe325b5870cb..d3c77d17eb967da65718e1bcda63a71d24a42159 100644 (file)
@@ -6654,6 +6654,7 @@ compl-generic     insert.txt      /*compl-generic*
 compl-keyword  insert.txt      /*compl-keyword*
 compl-omni     insert.txt      /*compl-omni*
 compl-omni-filetypes   insert.txt      /*compl-omni-filetypes*
+compl-register-words   insert.txt      /*compl-register-words*
 compl-spelling insert.txt      /*compl-spelling*
 compl-states   insert.txt      /*compl-states*
 compl-stop     insert.txt      /*compl-stop*
@@ -8425,6 +8426,7 @@ i_CTRL-X_CTRL-L   insert.txt      /*i_CTRL-X_CTRL-L*
 i_CTRL-X_CTRL-N        insert.txt      /*i_CTRL-X_CTRL-N*
 i_CTRL-X_CTRL-O        insert.txt      /*i_CTRL-X_CTRL-O*
 i_CTRL-X_CTRL-P        insert.txt      /*i_CTRL-X_CTRL-P*
+i_CTRL-X_CTRL-R        insert.txt      /*i_CTRL-X_CTRL-R*
 i_CTRL-X_CTRL-S        insert.txt      /*i_CTRL-X_CTRL-S*
 i_CTRL-X_CTRL-T        insert.txt      /*i_CTRL-X_CTRL-T*
 i_CTRL-X_CTRL-U        insert.txt      /*i_CTRL-X_CTRL-U*
index 5223687870c36417032fc710409fc1e445e14a05..578f5e8fe6080eacd6bbe97cb02c049c8ef9e47a 100644 (file)
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 9.1.  Last change: 2025 Apr 24
+*todo.txt*      For Vim version 9.1.  Last change: 2025 May 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -4764,7 +4764,6 @@ Insert mode completion/expansion:
 7   When expanding $HOME/dir with ^X^F keep the $HOME (with an option?).
 7   Add CTRL-X command in Insert mode like CTRL-X CTRL-N, that completes WORDS
     instead of words.
-8   Add CTRL-X CTRL-R: complete words from register contents.
 8   Add completion of previously inserted texts (like what CTRL-A does).
     Requires remembering a number of insertions.
 8   Add 'f' flag to 'complete': Expand file names.
index 72f43f06817b334a121dee52697e8f8015d68bed..250bd17ea6ffb5f82c160eee82dd3683b93ce9ca 100644 (file)
@@ -187,6 +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-T           words from a thesaurus
        CTRL-X CTRL-]           tags
        CTRL-X CTRL-V           Vim command line
index e03deedb8dd25d6f0ea0968caf1b410331894aff..662cb1771899cfb4359c0e1583bd81c106e155e9 100644 (file)
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2025 May 16
+*version9.txt*  For Vim version 9.1.  Last change: 2025 May 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41586,6 +41586,9 @@ Support for the |Tuple| data type in Vim script and Vim9 script.
 
 Support for a vertical |tabpanel| window similar to the 'tabline'.
 
+New Insert-mode completion: |i_CTRL-X_CTRL-R| to complete words from
+registers.
+
                                                        *changed-9.2*
 Changed~
 -------
index 46db57a458307d59075b0b48374d716524803d3e..ae14968bbe185a130bf44487b344642e9ed098a2 100644 (file)
@@ -338,6 +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
        etc.
 
 Long line support.                                     |'wrap'| |'linebreak'|
index 4d45e8ef7c90c49514c6cc35a3e26c776b31dd5f..366108883e6ef87c4ccd1d5df5e1706ef32f2aed 100644 (file)
@@ -929,6 +929,8 @@ doESCkey:
            break;
 
        case Ctrl_R:    // insert the contents of a register
+           if (ctrl_x_mode_register() && !ins_compl_active())
+               goto docomplete;
            ins_reg();
            auto_format(FALSE, TRUE);
            inserted_space = FALSE;
index 76fb8f3506c91b284809443ecd91a6eab973b05f..c7d6fd428c134ab5b4725c63e48c377e80778332 100644 (file)
@@ -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 @@ static char *ctrl_x_msgs[] =
     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 @@ static char *ctrl_x_mode_names[] = {
     NULL,                  // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
     "eval",
     "cmdline",
+    "register",
 };
 #endif
 
@@ -330,6 +333,8 @@ static int ctrl_x_mode_eval(void)
     { 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 @@ has_compl_option(int dict_opt)
 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 @@ vim_is_ctrl_x_key(int c)
                    || 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 @@ vim_is_ctrl_x_key(int c)
            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 @@ ins_compl_addfrommatch(void)
     static int
 set_ctrl_x_mode(int c)
 {
-    int retval = FALSE;
+    int            retval = FALSE;
 
     switch (c)
     {
@@ -2563,8 +2570,11 @@ set_ctrl_x_mode(int c)
            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
@@ -4783,6 +4793,83 @@ expand_cpt_function(callback_T *cb)
 }
 #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_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
            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 @@ 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()");
index e9ff62661b99eafeaeb8970e5eea4a6ec584b0ab..8965aca4c715e6e3dfb94a72f54f3f5e2c219ac7 100644 (file)
@@ -17,6 +17,7 @@ int ctrl_x_mode_spell(void);
 int ctrl_x_mode_line_or_eval(void);
 int ctrl_x_mode_not_default(void);
 int ctrl_x_mode_not_defined_yet(void);
+int ctrl_x_mode_register(void);
 int compl_status_adding(void);
 int compl_status_sol(void);
 int compl_status_local(void);
index 8bce0e45732838ed82843a611414806623ada514..2a2df51770536dee8602bcf83545c32ff23f63ac 100644 (file)
@@ -4521,4 +4521,85 @@ func Test_complete_match()
   delfunc TestComplete
 endfunc
 
+func Test_register_completion()
+  let @a = "completion test apple application"
+  let @b = "banana behavior better best"
+  let @c = "complete completion compliment computer"
+  let g:save_reg = ''
+  func GetItems()
+    let g:result = complete_info(['pum_visible'])
+  endfunc
+
+  new
+  call setline(1, "comp")
+  call cursor(1, 4)
+  call feedkeys("a\<C-X>\<C-R>\<C-N>\<C-N>\<Esc>", 'tx')
+  call assert_equal("compliment", getline(1))
+
+  inoremap <buffer><F2> <C-R>=GetItems()<CR>
+  call feedkeys("S\<C-X>\<C-R>\<F2>\<ESC>", 'tx')
+  call assert_equal(1, g:result['pum_visible'])
+
+  call setline(1, "app")
+  call cursor(1, 3)
+  call feedkeys("a\<C-X>\<C-R>\<C-N>\<Esc>", 'tx')
+  call assert_equal("application", getline(1))
+
+  " Test completion with case differences
+  set ignorecase
+  let @e = "TestCase UPPERCASE lowercase"
+  call setline(1, "testc")
+  call cursor(1, 5)
+  call feedkeys("a\<C-X>\<C-R>\<Esc>", 'tx')
+  call assert_equal("TestCase", getline(1))
+
+  " Test clipboard registers if available
+  if has('clipboard_working')
+    let g:save_reg = getreg('*')
+    call setreg('*', "clipboard selection unique words")
+    call setline(1, "uni")
+    call cursor(1, 3)
+    call feedkeys("a\<C-X>\<C-R>\<Esc>", 'tx')
+    call assert_equal("unique", getline(1))
+    call setreg('*', g:save_reg)
+
+    let g:save_reg = getreg('+')
+    call setreg('+', "system clipboard special content")
+    call setline(1, "spe")
+    call cursor(1, 3)
+    call feedkeys("a\<C-X>\<C-R>\<Esc>", 'tx')
+    call assert_equal("special", getline(1))
+    call setreg('+', g:save_reg)
+
+    call setreg('*', g:save_reg)
+    call setreg('a', "normal register")
+    call setreg('*', "clipboard mixed content")
+    call setline(1, "mix")
+    call cursor(1, 3)
+    call feedkeys("a\<C-X>\<C-R>\<Esc>", 'tx')
+    call assert_equal("mixed", getline(1))
+    call setreg('*', g:save_reg)
+  endif
+
+  " Test black hole register should be skipped
+  let @_ = "blackhole content should not appear"
+  call setline(1, "black")
+  call cursor(1, 5)
+  call feedkeys("a\<C-X>\<C-R>\<Esc>", 'tx')
+  call assert_equal("black", getline(1))
+
+  let @1 = "recent yank zero"
+  call setline(1, "ze")
+  call cursor(1, 2)
+  call feedkeys("a\<C-X>\<C-R>\<Esc>", 'tx')
+  call assert_equal("zero", getline(1))
+
+  " Clean up
+  bwipe!
+  delfunc GetItems
+  unlet g:result
+  unlet g:save_reg
+  set ignorecase&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab nofoldenable
index 637f178a035c4adae70e2fd8f992bb062f5f382e..ceedc5f7e3622e0e7faf4176261d2195163be0f2 100644 (file)
@@ -709,6 +709,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1408,
 /**/
     1407,
 /**/