diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-04-27 13:02:55 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2022-04-29 15:51:03 +0800 |
commit | 212349c100e9ada0c976bdaf1daf1aebec65edac (patch) | |
tree | dad7eb0010b211baeacc1ede2c046ff5671d1db1 | |
parent | 44269c73a32f13d39ead1fdc257a97fc18ae805c (diff) | |
download | rneovim-212349c100e9ada0c976bdaf1daf1aebec65edac.tar.gz rneovim-212349c100e9ada0c976bdaf1daf1aebec65edac.tar.bz2 rneovim-212349c100e9ada0c976bdaf1daf1aebec65edac.zip |
feat(edit): insert an unsimplified key using CTRL-SHIFT-V
This marks the following Vim patches as ported:
vim-patch:8.1.2333: with modifyOtherKeys CTRL-^ doesn't work
Problem: With modifyOtherKeys CTRL-^ doesn't work.
Solution: Handle the exception.
https://github.com/vim/vim/commit/828ffd596394f714270a01a55fc3f949a8bd9b35
vim-patch:8.1.2350: other text for CTRL-V in Insert mode with modifyOtherKeys
Problem: Other text for CTRL-V in Insert mode with modifyOtherKeys.
Solution: Convert the Escape sequence back to key as if modifyOtherKeys is
not set, and use CTRL-SHIFT-V to get the Escape sequence itself.
(closes vim/vim#5254)
https://github.com/vim/vim/commit/fc4ea2a72d36de1196a3ce17352e72f8fe90f4bb
vim-patch:8.2.2084: CTRL-V U doesn't work to enter a Unicode character
Problem: CTRL-V U doesn't work to enter a Unicode character when
modifyOtherKeys is effective. (Ken Takata)
Solution: Add a flag to get_literal() for the shift key. (closes vim/vim#7413)
https://github.com/vim/vim/commit/0684e36a7ee0743f2889698fb8e0e14f7acae423
Omit getcmdkeycmd() change as it depends on Vim patch 8.2.2062, which
may introduce a potential breakage.
-rw-r--r-- | runtime/doc/cmdline.txt | 7 | ||||
-rw-r--r-- | runtime/doc/insert.txt | 12 | ||||
-rw-r--r-- | src/nvim/edit.c | 20 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 6 | ||||
-rw-r--r-- | src/nvim/getchar.c | 35 | ||||
-rw-r--r-- | src/nvim/normal.c | 4 | ||||
-rw-r--r-- | test/functional/editor/mode_cmdline_spec.lua | 105 | ||||
-rw-r--r-- | test/functional/editor/mode_insert_spec.lua | 5 |
8 files changed, 118 insertions, 76 deletions
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 9fa2034718..6228c9238f 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -67,12 +67,19 @@ CTRL-V Insert next non-digit literally. Up to three digits form the decimal value of a single byte. The non-digit and the three digits are not considered for mapping. This works the same way as in Insert mode (see above, |i_CTRL-V|). + For special keys, the CTRL modifier may be included into the + key to produce a control character. If there is no control + character for the key then its |key-notation| is inserted. Note: Under Windows CTRL-V is often mapped to paste text. Use CTRL-Q instead then. *c_CTRL-Q* CTRL-Q Same as CTRL-V. But with some terminals it is used for control flow, it doesn't work then. +CTRL-SHIFT-V *c_CTRL-SHIFT-V* *c_CTRL-SHIFT-Q* +CTRL-SHIFT-Q Works just like CTRL-V, but do not try to include the CTRL + modifier into the key. + *c_<Left>* *c_Left* <Left> cursor left *c_<Right>* *c_Right* diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 9db44eaaa0..7f6662089d 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -192,12 +192,14 @@ CTRL-D Delete one shiftwidth of indent at the start of the current label. *i_CTRL-V* -CTRL-V Insert next non-digit literally. For special keys, the - terminal code is inserted. It's also possible to enter the - decimal, octal or hexadecimal value of a character +CTRL-V Insert next non-digit literally. It's also possible to enter + the decimal, octal or hexadecimal value of a character |i_CTRL-V_digit|. The characters typed right after CTRL-V are not considered for mapping. + For special keys, the CTRL modifier may be included into the + key to produce a control character. If there is no control + character for the key then its |key-notation| is inserted. Note: When CTRL-V is mapped (e.g., to paste text) you can often use CTRL-Q instead |i_CTRL-Q|. @@ -206,6 +208,10 @@ CTRL-Q Same as CTRL-V. Note: Some terminal connections may eat CTRL-Q, it doesn't work then. It does work in the GUI. +CTRL-SHIFT-V *i_CTRL-SHIFT-V* *i_CTRL-SHIFT-Q* +CTRL-SHIFT-Q Works just like CTRL-V, but do not try to include the CTRL + modifier into the key. + CTRL-X Enter CTRL-X mode. This is a sub-mode where commands can be given to complete words or scroll the window. See |i_CTRL-X| and |ins-completion|. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 48436f1115..3e3648e61f 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1587,7 +1587,8 @@ static void ins_ctrl_v(void) add_to_showcmd_c(Ctrl_V); - c = get_literal(); + // Do not include modifiers into the key for CTRL-SHIFT-V. + c = get_literal(mod_mask & MOD_MASK_SHIFT); if (did_putchar) { // when the line fits in 'columns' the '^' is at the start of the next // line and will not removed by the redraw @@ -5612,13 +5613,13 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len) return m; } -/* - * Next character is interpreted literally. - * A one, two or three digit decimal number is interpreted as its byte value. - * If one or two digits are entered, the next character is given to vungetc(). - * For Unicode a character > 255 may be returned. - */ -int get_literal(void) +/// Next character is interpreted literally. +/// A one, two or three digit decimal number is interpreted as its byte value. +/// If one or two digits are entered, the next character is given to vungetc(). +/// For Unicode a character > 255 may be returned. +/// +/// @param no_simplify do not include modifiers into the key +int get_literal(bool no_simplify) { int cc; int nc; @@ -5636,6 +5637,9 @@ int get_literal(void) i = 0; for (;;) { nc = plain_vgetc(); + if (!no_simplify) { + nc = merge_modifiers(nc); + } if ((mod_mask & ~MOD_MASK_SHIFT) != 0) { // A character with non-Shift modifiers should not be a valid // character for i_CTRL-V_digit. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index b7d75855d6..ea5bfc9a36 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2208,7 +2208,11 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_Q: s->ignore_drag_release = true; putcmdline('^', true); - s->c = get_literal(); // get next (two) character(s) + + // Get next (two) characters. + // Do not include modifiers into the key for CTRL-SHIFT-V. + s->c = get_literal(mod_mask & MOD_MASK_SHIFT); + s->do_abbr = false; // don't do abbreviation now ccline.special_char = NUL; // may need to remove ^ when composing char was typed diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b25c59636f..f92aa14c78 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1442,6 +1442,27 @@ static void updatescript(int c) } } +/// Merge "mod_mask" into "c_arg" +int merge_modifiers(int c_arg) +{ + int c = c_arg; + + if (mod_mask & MOD_MASK_CTRL) { + if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) { + c &= 0x1f; + mod_mask &= ~MOD_MASK_CTRL; + if (c == 0) { + c = K_ZERO; + } + } else if (c == '6') { + // CTRL-6 is equivalent to CTRL-^ + c = 0x1e; + mod_mask &= ~MOD_MASK_CTRL; + } + } + return c; +} + /// Get the next input character. /// Can return a special key or a multi-byte character. /// Can return NUL when called recursively, use safe_vgetc() if that's not @@ -1603,19 +1624,7 @@ int vgetc(void) // A modifier was not used for a mapping, apply it to ASCII // keys. Shift would already have been applied. - if (mod_mask & MOD_MASK_CTRL) { - if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) { - c &= 0x1f; - mod_mask &= ~MOD_MASK_CTRL; - if (c == 0) { - c = K_ZERO; - } - } else if (c == '6') { - // CTRL-6 is equivalent to CTRL-^ - c = 0x1e; - mod_mask &= ~MOD_MASK_CTRL; - } - } + c = merge_modifiers(c); // If mappings are enabled (i.e., not Ctrl-v) and the user directly typed // something with a meta- or alt- modifier that was not mapped, interpret diff --git a/src/nvim/normal.c b/src/nvim/normal.c index ed5f13d00a..73ea995d8f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5154,7 +5154,7 @@ static void nv_replace(cmdarg_T *cap) // get another character if (cap->nchar == Ctrl_V) { had_ctrl_v = Ctrl_V; - cap->nchar = get_literal(); + cap->nchar = get_literal(false); // Don't redo a multibyte character with CTRL-V. if (cap->nchar > DEL) { had_ctrl_v = NUL; @@ -5369,7 +5369,7 @@ static void nv_vreplace(cmdarg_T *cap) emsg(_(e_modifiable)); } else { if (cap->extra_char == Ctrl_V) { // get another character - cap->extra_char = get_literal(); + cap->extra_char = get_literal(false); } stuffcharReadbuff(cap->extra_char); stuffcharReadbuff(ESC); diff --git a/test/functional/editor/mode_cmdline_spec.lua b/test/functional/editor/mode_cmdline_spec.lua index 0f7d821bb5..50cc5e17ee 100644 --- a/test/functional/editor/mode_cmdline_spec.lua +++ b/test/functional/editor/mode_cmdline_spec.lua @@ -3,67 +3,74 @@ local helpers = require('test.functional.helpers')(after_each) local clear, insert, funcs, eq, feed = helpers.clear, helpers.insert, helpers.funcs, helpers.eq, helpers.feed +local eval = helpers.eval local meths = helpers.meths -describe('cmdline CTRL-R', function() +describe('cmdline', function() before_each(clear) - it('pasting non-special register inserts <CR> *between* lines', function() - insert([[ - line1abc - line2somemoretext - ]]) - -- Yank 2 lines linewise, then paste to cmdline. - feed([[<C-\><C-N>gg0yj:<C-R>0]]) - -- <CR> inserted between lines, NOT after the final line. - eq('line1abc\rline2somemoretext', funcs.getcmdline()) + describe('Ctrl-R', function() + it('pasting non-special register inserts <CR> *between* lines', function() + insert([[ + line1abc + line2somemoretext + ]]) + -- Yank 2 lines linewise, then paste to cmdline. + feed([[<C-\><C-N>gg0yj:<C-R>0]]) + -- <CR> inserted between lines, NOT after the final line. + eq('line1abc\rline2somemoretext', funcs.getcmdline()) - -- Yank 2 lines charwise, then paste to cmdline. - feed([[<C-\><C-N>gg05lyvj:<C-R>0]]) - -- <CR> inserted between lines, NOT after the final line. - eq('abc\rline2', funcs.getcmdline()) + -- Yank 2 lines charwise, then paste to cmdline. + feed([[<C-\><C-N>gg05lyvj:<C-R>0]]) + -- <CR> inserted between lines, NOT after the final line. + eq('abc\rline2', funcs.getcmdline()) - -- Yank 1 line linewise, then paste to cmdline. - feed([[<C-\><C-N>ggyy:<C-R>0]]) - -- No <CR> inserted. - eq('line1abc', funcs.getcmdline()) - end) + -- Yank 1 line linewise, then paste to cmdline. + feed([[<C-\><C-N>ggyy:<C-R>0]]) + -- No <CR> inserted. + eq('line1abc', funcs.getcmdline()) + end) - it('pasting special register inserts <CR>, <NL>', function() - feed([[:<C-R>="foo\nbar\rbaz"<CR>]]) - eq('foo\nbar\rbaz', funcs.getcmdline()) + it('pasting special register inserts <CR>, <NL>', function() + feed([[:<C-R>="foo\nbar\rbaz"<CR>]]) + eq('foo\nbar\rbaz', funcs.getcmdline()) + end) end) -end) -describe('cmdline history', function() - before_each(clear) + it('Ctrl-Shift-V supports entering unsimplified key notations', function() + feed(':"<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><CR>') - it('correctly clears start of the history', function() - -- Regression test: check absence of the memory leak when clearing start of - -- the history using ex_getln.c/clr_history(). - eq(1, funcs.histadd(':', 'foo')) - eq(1, funcs.histdel(':')) - eq('', funcs.histget(':', -1)) + eq('"<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>', eval('@:')) end) - it('correctly clears end of the history', function() - -- Regression test: check absence of the memory leak when clearing end of - -- the history using ex_getln.c/clr_history(). - meths.set_option('history', 1) - eq(1, funcs.histadd(':', 'foo')) - eq(1, funcs.histdel(':')) - eq('', funcs.histget(':', -1)) - end) + describe('history', function() + it('correctly clears start of the history', function() + -- Regression test: check absence of the memory leak when clearing start of + -- the history using ex_getln.c/clr_history(). + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) + + it('correctly clears end of the history', function() + -- Regression test: check absence of the memory leak when clearing end of + -- the history using ex_getln.c/clr_history(). + meths.set_option('history', 1) + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) - it('correctly removes item from history', function() - -- Regression test: check that ex_getln.c/del_history_idx() correctly clears - -- history index after removing history entry. If it does not then deleting - -- history will result in a double free. - eq(1, funcs.histadd(':', 'foo')) - eq(1, funcs.histadd(':', 'bar')) - eq(1, funcs.histadd(':', 'baz')) - eq(1, funcs.histdel(':', -2)) - eq(1, funcs.histdel(':')) - eq('', funcs.histget(':', -1)) + it('correctly removes item from history', function() + -- Regression test: check that ex_getln.c/del_history_idx() correctly clears + -- history index after removing history entry. If it does not then deleting + -- history will result in a double free. + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histadd(':', 'bar')) + eq(1, funcs.histadd(':', 'baz')) + eq(1, funcs.histdel(':', -2)) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) end) end) diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua index c38acbe96a..684dee69db 100644 --- a/test/functional/editor/mode_insert_spec.lua +++ b/test/functional/editor/mode_insert_spec.lua @@ -131,6 +131,11 @@ describe('insert-mode', function() end) end) + it('Ctrl-Shift-V supports entering unsimplified key notations', function() + feed('i<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><Esc>') + expect('<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>') + end) + describe([[With 'insertmode', Insert mode is not re-entered immediately after <C-L>]], function() before_each(function() command('set insertmode') |