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:
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))