neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 79bfeecdb483c683e79abca3e9a1bd98b3a30b0f
parent b4d21f141cf1dff0abdb2f43f0ebca105a171441
Author: Shadman <shadmansaleh3@gmail.com>
Date:   Tue,  2 Sep 2025 10:05:16 +0600

feat(editor)!: insert-mode ctrl-r should work like paste #35477

Problem:
insert-mode ctrl-r input is treated like raw user input, which is almost
never useful. This means any newlines in the input are affected by
autoindent, etc., which is:
- slow
- usually breaks the formatting of the input

Solution:
- ctrl-r should be treated like a paste, not user-input.
- does not affect `<c-r>=`, so `<c-r>=@x` can still be used to get the
  old behavior.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Diffstat:
Mruntime/doc/cmdline.txt | 21+++++++++++++--------
Mruntime/doc/insert.txt | 19++++++++++++-------
Mruntime/doc/news.txt | 3++-
Mruntime/doc/vim_diff.txt | 2++
Msrc/nvim/edit.c | 8+++++++-
Msrc/nvim/ops.h | 2+-
Mtest/functional/editor/mode_insert_spec.lua | 30++++++++++++++++++++++++++++++
Mtest/old/testdir/test_autocmd.vim | 2+-
Mtest/old/testdir/test_cmdline.vim | 2+-
Mtest/old/testdir/test_ins_complete.vim | 2+-
10 files changed, 70 insertions(+), 21 deletions(-)

diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt @@ -142,13 +142,16 @@ CTRL-R {register} *c_CTRL-R* *c_<C-R>* typing CTRL-R and the second character '"' will be displayed to indicate that you are expected to enter the name of a register. - The text is inserted as if you typed it, but mappings and - abbreviations are not used. Command-line completion through - 'wildchar' is not triggered though. And characters that end - the command line are inserted literally (<Esc>, <CR>, <NL>, - <C-C>). A <BS> or CTRL-W could still end the command line - though, and remaining characters will then be interpreted in - another mode, which might not be what you intended. + When used with named or clipboard registers (A-Z,a-z,0-9,+) + text is inserted literally like pasting with "p". For other + registers, the text is inserted as if you typed it, but + mappings and abbreviations are not used. Command-line + completion through 'wildchar' is not triggered though. And + characters that end the command line are inserted literally + (<Esc>, <CR>, <NL>, <C-C>). A <BS> or CTRL-W could still end + the command line though, and remaining characters will then be + interpreted in another mode, which might not be what you + intended. Special registers: '"' the unnamed register, containing the text of the last delete or yank @@ -176,7 +179,9 @@ CTRL-R {register} *c_CTRL-R* *c_<C-R>* sure the expression evaluates to an empty string. E.g.: > <C-R><C-R>=setcmdpos(2)[-1]<CR> -< See |registers| about registers. +< You can use this to insert a register as + typed with CTRL-R =@reg. + See |registers| about registers. Implementation detail: When using the |expression| register and invoking setcmdpos(), this sets the position before inserting the resulting string. Use CTRL-R CTRL-R to set the diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt @@ -101,14 +101,17 @@ CTRL-N Find next keyword (see |i_CTRL-N|). CTRL-P Find previous keyword (see |i_CTRL-P|). CTRL-R {register} *i_CTRL-R* - Insert the contents of a register. Between typing CTRL-R and + Insert the contents of a register. Between typing CTRL-R and the second character, '"' will be displayed to indicate that - you are expected to enter the name of a register. - The text is inserted as if you typed it, but mappings and - abbreviations are not used. If you have options like - 'textwidth', 'formatoptions', or 'autoindent' set, this will - influence what will be inserted. This is different from what - happens with the "p" command and pasting with the mouse. + you are expected to enter the name of a register. When used + with When used with named or clipboard registers + (A-Z,a-z,0-9,+) text is inserted literally like pasting with + "p". For other registers, the text is inserted as if you typed + it, but mappings and abbreviations are not used. If you have + options like 'textwidth', 'formatoptions', or 'autoindent' + set, this will influence what will be inserted. This is + different from what happens with the "p" command and pasting + with the mouse. Special registers: '"' the unnamed register, containing the text of the last delete or yank @@ -131,6 +134,8 @@ CTRL-R {register} *i_CTRL-R* special keys. E.g., you can use this to move the cursor up: CTRL-R ="\<Up>" + you can use this to insert a register as + typed with CTRL-R =@reg. Use CTRL-R CTRL-R to insert text literally. When the result is a |List| the items are used as lines. They can have line breaks inside diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt @@ -68,7 +68,8 @@ DIAGNOSTICS EDITOR -• todo +• |i_CTRL-R| inserts named registers (A-Z,a-z,0-9) literally like pasting instead of + as typed. To get the old behavior you can use `<C-R>=@x`. EVENTS diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt @@ -316,6 +316,8 @@ Commands: Editor: - |prompt-buffer| supports multiline input/paste, undo/redo, and o/O normal commands. +- |i_CTRL-R| inserts named registers (A-Z,a-z,0-9) literally like pasting instead of + as typed. To get the old behavior you can use `<C-R>=@x`. Events (autocommands): - Fixed inconsistent behavior in execution of nested autocommands #23368 diff --git a/src/nvim/edit.c b/src/nvim/edit.c @@ -2854,6 +2854,8 @@ static void ins_reg(void) vim_beep(kOptBoFlagRegister); need_redraw = true; // remove the '"' } else { + yankreg_T *reg = get_yank_register(regname, YREG_PASTE); + if (literally == Ctrl_O || literally == Ctrl_P) { // Append the command to the redo buffer. AppendCharToRedobuff(Ctrl_R); @@ -2862,7 +2864,11 @@ static void ins_reg(void) do_put(regname, NULL, BACKWARD, 1, (literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND); - } else if (insert_reg(regname, NULL, literally) == FAIL) { + } else if (reg->y_size > 1 && is_literal_register(regname)) { + AppendCharToRedobuff(Ctrl_R); + AppendCharToRedobuff(regname); + do_put(regname, NULL, BACKWARD, 1, PUT_CURSEND); + } else if (insert_reg(regname, NULL, !!literally) == FAIL) { vim_beep(kOptBoFlagRegister); need_redraw = true; // remove the '"' } else if (stop_insert_mode) { diff --git a/src/nvim/ops.h b/src/nvim/ops.h @@ -152,7 +152,7 @@ static inline int op_reg_index(const int regname) static inline bool is_literal_register(const int regname) FUNC_ATTR_CONST { - return regname == '*' || regname == '+'; + return regname == '*' || regname == '+' || ASCII_ISALNUM(regname); } EXTERN LuaRef repeat_luaref INIT( = LUA_NOREF); ///< LuaRef for "." diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua @@ -83,6 +83,36 @@ describe('insert-mode', function() {5:-- INSERT --} | ]]) end) + + it('inserts named registers literally', function() + local screen = Screen.new(50, 6) + -- regular text without special charecter command + command('let @a = "test"') + feed('i<C-R>a<ESC>') + screen:expect([[ + tes^t | + {1:~ }|*4 + | + ]]) + + -- text with backspace character gets written literally by default + command('let @a = "test\\<C-H>"') + feed('cc<C-R>a<ESC>') + screen:expect([[ + test{18:^^H} | + {1:~ }|*4 + | + ]]) + + -- =@reg<CR> can be used to get effect of keypress + command('let @a = "test\\<C-H>"') + feed('cc<C-R>=@a<CR><ESC>') + screen:expect([[ + te^s | + {1:~ }|*4 + | + ]]) + end) end) describe('Ctrl-O', function() diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim @@ -2090,7 +2090,7 @@ func Test_Cmdline() let g:log = [] let @r = 'abc' - call feedkeys(":0\<C-R>r1\<C-R>\<C-O>r2\<C-R>\<C-R>r3\<Esc>", 'xt') + call feedkeys(":0\<C-R>=@r\<CR>1\<C-R>\<C-O>r2\<C-R>\<C-R>r3\<Esc>", 'xt') call assert_equal([ \ '0', \ '0a', diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim @@ -905,7 +905,7 @@ func Test_cmdline_paste() " Test for pasting register containing CTRL-H using CTRL-R and CTRL-R CTRL-R let @a = "xy\<C-H>z" - call feedkeys(":\"\<C-R>a\<CR>", 'xt') + call feedkeys(":\"\<C-R>=@a\<CR>\<CR>", 'xt') call assert_equal('"xz', @:) call feedkeys(":\"\<C-R>\<C-R>a\<CR>", 'xt') call assert_equal("\"xy\<C-H>z", @:) diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim @@ -1523,7 +1523,7 @@ func Test_complete_reginsert() exe "normal Goa\<C-P>\<C-R>=\"\\<C-P>\"\<CR>" call assert_equal('a123', getline(5)) let @r = "\<C-P>\<C-P>" - exe "normal GCa\<C-P>\<C-R>r" + exe "normal GCa\<C-P>\<C-R>=@r\<CR>" call assert_equal('a12', getline(5)) exe "normal GCa\<C-P>\<C-R>=\"x\"\<CR>" call assert_equal('a1234x', getline(5))