diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-04-07 22:40:01 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-07 22:40:01 +0800 |
commit | abc157a6fd5ed2f09271ee3dd75d23d9ec3e0313 (patch) | |
tree | dde71e2d365e1d1eb49453eaf83fd3e03efc2619 | |
parent | dc9e436986bec15b027c2a8d78782f514c046a8b (diff) | |
parent | 64802da6c4304a2700d9471d17c0aae143d9aab1 (diff) | |
download | rneovim-abc157a6fd5ed2f09271ee3dd75d23d9ec3e0313.tar.gz rneovim-abc157a6fd5ed2f09271ee3dd75d23d9ec3e0313.tar.bz2 rneovim-abc157a6fd5ed2f09271ee3dd75d23d9ec3e0313.zip |
Merge pull request #18021 from zeertzjq/fix-clearing-reg-executing
Fix clearing of reg_executing
-rw-r--r-- | src/nvim/ex_docmd.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_docmd.h | 1 | ||||
-rw-r--r-- | src/nvim/getchar.c | 21 | ||||
-rw-r--r-- | src/nvim/globals.h | 2 | ||||
-rw-r--r-- | src/nvim/state.c | 5 | ||||
-rw-r--r-- | src/nvim/testdir/test_registers.vim | 18 | ||||
-rw-r--r-- | test/functional/editor/macro_spec.lua | 47 |
7 files changed, 90 insertions, 8 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index bb6f3ba395..1f17101aca 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1248,6 +1248,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe const int save_msg_scroll = msg_scroll; cmdmod_T save_cmdmod; const int save_reg_executing = reg_executing; + const bool save_pending_end_reg_executing = pending_end_reg_executing; char_u *cmd; memset(&ea, 0, sizeof(ea)); @@ -2018,6 +2019,7 @@ doend: undo_cmdmod(&ea, save_msg_scroll); cmdmod = save_cmdmod; reg_executing = save_reg_executing; + pending_end_reg_executing = save_pending_end_reg_executing; if (ea.did_sandbox) { sandbox--; @@ -8529,6 +8531,7 @@ bool save_current_state(save_state_T *sst) sst->save_finish_op = finish_op; sst->save_opcount = opcount; sst->save_reg_executing = reg_executing; + sst->save_pending_end_reg_executing = pending_end_reg_executing; msg_scroll = false; // no msg scrolling in Normal mode restart_edit = 0; // don't go to Insert mode @@ -8559,6 +8562,7 @@ void restore_current_state(save_state_T *sst) finish_op = sst->save_finish_op; opcount = sst->save_opcount; reg_executing = sst->save_reg_executing; + pending_end_reg_executing = sst->save_pending_end_reg_executing; // don't reset msg_didout now msg_didout |= sst->save_msg_didout; diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index be9f97e27d..874e0e599e 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -29,6 +29,7 @@ typedef struct { bool save_finish_op; long save_opcount; int save_reg_executing; + bool save_pending_end_reg_executing; tasave_T tabuf; } save_state_T; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index eddd5ccd14..299385cb17 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2060,7 +2060,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) } /// unget one character (can only be done once!) -/// If the character was stuffed, vgetc() will get it next time it was called. +/// If the character was stuffed, vgetc() will get it next time it is called. /// Otherwise vgetc() will only get it when the stuff buffer is empty. void vungetc(int c) { @@ -2072,6 +2072,20 @@ void vungetc(int c) old_KeyStuffed = KeyStuffed; } +/// When peeking and not getting a character, reg_executing cannot be cleared +/// yet, so set a flag to clear it later. +void check_end_reg_executing(bool advance) +{ + if (reg_executing != 0 && (typebuf.tb_maplen == 0 || pending_end_reg_executing)) { + if (advance) { + reg_executing = 0; + pending_end_reg_executing = false; + } else { + pending_end_reg_executing = true; + } + } +} + /// Gets a byte: /// 1. from the stuffbuffer /// This is used for abbreviated commands like "D" -> "d$". @@ -2126,9 +2140,7 @@ static int vgetorpeek(bool advance) init_typebuf(); start_stuff(); - if (advance && typebuf.tb_maplen == 0) { - reg_executing = 0; - } + check_end_reg_executing(advance); do { // get a character: 1. from the stuffbuffer if (typeahead_char != 0) { @@ -2155,6 +2167,7 @@ static int vgetorpeek(bool advance) // If a mapped key sequence is found we go back to the start to // try re-mapping. for (;;) { + check_end_reg_executing(advance); // os_breakcheck() is slow, don't use it too often when // inside a mapping. But call it each time for typed // characters. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index ace7647b35..4aa49337cf 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -643,6 +643,8 @@ EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p. EXTERN int reg_recording INIT(= 0); // register for recording or zero EXTERN int reg_executing INIT(= 0); // register being executed or zero +// Flag set when peeking a character and found the end of executed register +EXTERN bool pending_end_reg_executing INIT(= false); EXTERN int reg_recorded INIT(= 0); // last recorded register or zero EXTERN int no_mapping INIT(= false); // currently no mapping allowed diff --git a/src/nvim/state.c b/src/nvim/state.c index 3a7636085b..34e3ddf654 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -40,7 +40,7 @@ void state_enter(VimState *s) int key; getkey: - // Expand mappings first by calling vpeekc() directly. + // Apply mappings first by calling vpeekc() directly. // - If vpeekc() returns non-NUL, there is a character already available for processing, so // don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence. // - If vpeekc() returns NUL, vgetc() will block, and there are three cases: @@ -76,6 +76,9 @@ getkey: } if (key == K_EVENT) { + // An event handler may use the value of reg_executing. + // Clear it if it should be cleared when getting the next character. + check_end_reg_executing(true); may_sync_undo(); } diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index f78b748d71..c623edd5a1 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -690,5 +690,23 @@ func Test_record_in_select_mode() bwipe! endfunc +func Test_end_reg_executing() + nnoremap s <Nop> + let @a = 's' + call feedkeys("@aqaq\<Esc>", 'tx') + call assert_equal('', @a) + call assert_equal('', getline(1)) + + call setline(1, 'aaa') + nnoremap s qa + let @a = 'fa' + call feedkeys("@asq\<Esc>", 'tx') + call assert_equal('', @a) + call assert_equal('aaa', getline(1)) + + nunmap s + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/functional/editor/macro_spec.lua b/test/functional/editor/macro_spec.lua index c0c9256af2..d4cf6b28fd 100644 --- a/test/functional/editor/macro_spec.lua +++ b/test/functional/editor/macro_spec.lua @@ -6,11 +6,14 @@ local feed = helpers.feed local clear = helpers.clear local expect = helpers.expect local command = helpers.command +local funcs = helpers.funcs +local meths = helpers.meths local insert = helpers.insert local curbufmeths = helpers.curbufmeths +before_each(clear) + describe('macros', function() - before_each(clear) it('can be recorded and replayed', function() feed('qiahello<esc>q') expect('hello') @@ -47,9 +50,47 @@ hello]] end) end) -describe('reg_recorded()', function() - before_each(clear) +describe('immediately after a macro has finished executing,', function() + before_each(function() + command([[let @a = 'gg0']]) + end) + + describe('reg_executing() from RPC returns an empty string', function() + it('if the macro does not end with a <Nop> mapping', function() + feed('@a') + eq('', funcs.reg_executing()) + end) + + it('if the macro ends with a <Nop> mapping', function() + command('nnoremap 0 <Nop>') + feed('@a') + eq('', funcs.reg_executing()) + end) + end) + describe('characters from a mapping are not treated as a part of the macro #18015', function() + before_each(function() + command('nnoremap s qa') + end) + + it('if the macro does not end with a <Nop> mapping', function() + feed('@asq') -- "q" from "s" mapping should start recording a macro instead of being no-op + eq({mode = 'n', blocking = false}, meths.get_mode()) + expect('') + eq('', eval('@a')) + end) + + it('if the macro ends with a <Nop> mapping', function() + command('nnoremap 0 <Nop>') + feed('@asq') -- "q" from "s" mapping should start recording a macro instead of being no-op + eq({mode = 'n', blocking = false}, meths.get_mode()) + expect('') + eq('', eval('@a')) + end) + end) +end) + +describe('reg_recorded()', function() it('returns the correct value', function() feed [[qqyyq]] eq('q', eval('reg_recorded()')) |