# HG changeset patch # User Bram Moolenaar # Date 1599596103 -7200 # Node ID ea87cd1fac757a089a3a051701a2b19b32805c83 # Parent 93ea001b0816db2149fa81cffcc5e1f70ca89d6d patch 8.2.1636: get stuck if a popup filter causes an error Commit: https://github.com/vim/vim/commit/6defa7bf0a8935cc44f3dc12e9c87bbb40f190b7 Author: Bram Moolenaar Date: Tue Sep 8 22:06:44 2020 +0200 patch 8.2.1636: get stuck if a popup filter causes an error Problem: Get stuck if a popup filter causes an error. Solution: Check whether the function can be called and does not cause an error. (closes #6902) diff --git a/src/popupwin.c b/src/popupwin.c --- a/src/popupwin.c +++ b/src/popupwin.c @@ -3126,6 +3126,7 @@ invoke_popup_filter(win_T *wp, int c) typval_T argv[3]; char_u buf[NUMBUFLEN]; linenr_T old_lnum = wp->w_cursor.lnum; + int prev_called_emsg = called_emsg; // Emergency exit: CTRL-C closes the popup. if (c == Ctrl_C) @@ -3151,10 +3152,35 @@ invoke_popup_filter(win_T *wp, int c) argv[2].v_type = VAR_UNKNOWN; // NOTE: The callback might close the popup and make "wp" invalid. - call_callback(&wp->w_filter_cb, -1, &rettv, 2, argv); + if (call_callback(&wp->w_filter_cb, -1, &rettv, 2, argv) == FAIL) + { + // Cannot call the function, close the popup to avoid that the filter + // eats keys and the user can't get out. + popup_close_with_retval(wp, -1); + return 1; + } + if (win_valid_popup(wp) && old_lnum != wp->w_cursor.lnum) popup_highlight_curline(wp); - res = tv_get_bool(&rettv); + + // If an error was given always return FALSE, so that keys are not + // consumed and the user can type something. + // If we get three errors in a row then close the popup. Decrement the + // error count by 1/10 if there are no errors, thus allowing up to 1 in + // 10 calls to cause an error. + if (win_valid_popup(wp) && called_emsg > prev_called_emsg) + { + wp->w_filter_errors += 10; + if (wp->w_filter_errors >= 30) + popup_close_with_retval(wp, -1); + res = FALSE; + } + else + { + if (win_valid_popup(wp) && wp->w_filter_errors > 0) + --wp->w_filter_errors; + res = tv_get_bool(&rettv); + } vim_free(argv[1].vval.v_string); clear_tv(&rettv); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -3338,6 +3338,7 @@ struct window_S // with "cursorline" set callback_T w_close_cb; // popup close callback callback_T w_filter_cb; // popup filter callback + int w_filter_errors; // popup filter error count int w_filter_mode; // mode when filter callback is used win_T *w_popup_curwin; // close popup if curwin differs diff --git a/src/testdir/dumps/Test_popupwin_three_errors_1.dump b/src/testdir/dumps/Test_popupwin_three_errors_1.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_three_errors_1.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @27|o+0#0000001#ffd7ff255|n|e| |t|w|o| |t|h|r|e@1|.@2| +0#4040ff13#ffffff0@29 +|~| @73 +|~| @73 +|~| @73 +|E+0#ffffff16#e000002|8|9|6|:| |A|r|g|u|m|e|n|t| |o|f| |f|i|l|t|e|r|(|)| |m|u|s|t| |b|e| |a| |L|i|s|t|,| |D|i|c|t|i|o|n|a|r|y| |o|r| |B|l|o|b| +0#0000000#ffffff0@13 +@57|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_three_errors_2.dump b/src/testdir/dumps/Test_popupwin_three_errors_2.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_three_errors_2.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|E+0#ffffff16#e000002|8|9|6|:| |A|r|g|u|m|e|n|t| |o|f| |f|i|l|t|e|r|(|)| |m|u|s|t| |b|e| |a| |L|i|s|t|,| |D|i|c|t|i|o|n|a|r|y| |o|r| |B|l|o|b| +0#0000000#ffffff0@13 +@57|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_wrong_name.dump b/src/testdir/dumps/Test_popupwin_wrong_name.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_wrong_name.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|E+0#ffffff16#e000002|1@1|7|:| |U|n|k|n|o|w|n| |f|u|n|c|t|i|o|n|:| |N|o|S|u|c|h|F|u|n|c| +0#0000000#ffffff0@22|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -3516,7 +3516,44 @@ func Test_popupwin_filter_close_ctrl_c() call VerifyScreenDump(buf, 'Test_popupwin_ctrl_c', {}) call StopVimInTerminal(buf) - call delete('XtestPopupCorners') + call delete('XtestPopupCtrlC') +endfunc + +func Test_popupwin_filter_close_wrong_name() + CheckScreendump + + let lines =<< trim END + call popup_create('one two three...', {'filter': 'NoSuchFunc'}) + END + call writefile(lines, 'XtestPopupWrongName') + + let buf = RunVimInTerminal('-S XtestPopupWrongName', #{rows: 10}) + + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_popupwin_wrong_name', {}) + + call StopVimInTerminal(buf) + call delete('XtestPopupWrongName') +endfunc + +func Test_popupwin_filter_close_three_errors() + CheckScreendump + + let lines =<< trim END + set cmdheight=2 + call popup_create('one two three...', {'filter': 'filter'}) + END + call writefile(lines, 'XtestPopupThreeErrors') + + let buf = RunVimInTerminal('-S XtestPopupThreeErrors', #{rows: 10}) + + call term_sendkeys(buf, "jj") + call VerifyScreenDump(buf, 'Test_popupwin_three_errors_1', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_popupwin_three_errors_2', {}) + + call StopVimInTerminal(buf) + call delete('XtestPopupThreeErrors') endfunc func Test_popupwin_atcursor_far_right() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1636, +/**/ 1635, /**/ 1634,