]> granicus.if.org Git - vim/commitdiff
patch 8.2.1636: get stuck if a popup filter causes an error v8.2.1636
authorBram Moolenaar <Bram@vim.org>
Tue, 8 Sep 2020 20:06:44 +0000 (22:06 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 8 Sep 2020 20:06:44 +0000 (22:06 +0200)
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)

src/popupwin.c
src/structs.h
src/testdir/dumps/Test_popupwin_three_errors_1.dump [new file with mode: 0644]
src/testdir/dumps/Test_popupwin_three_errors_2.dump [new file with mode: 0644]
src/testdir/dumps/Test_popupwin_wrong_name.dump [new file with mode: 0644]
src/testdir/test_popupwin.vim
src/version.c

index 953e76cc7e3679456160fd25f117d29cf69c89f2..23f8efc8e4eae2e3e3cf08ef8335d6020677cbae 100644 (file)
@@ -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);
index 528c247b1478466374940deb415a415f9a2bd3fe..dba9be65d634ae5cd0f677104b41f3c9fd6a71d2 100644 (file)
@@ -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 (file)
index 0000000..2bf9a54
--- /dev/null
@@ -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 (file)
index 0000000..09550a2
--- /dev/null
@@ -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 (file)
index 0000000..e63ced5
--- /dev/null
@@ -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| 
index a92dc25505a96a79081520121c684775cab6ab7c..954086cae4d0a9bbe942890c57797f5cb390f518 100644 (file)
@@ -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()
index 2a2de13bbfb4a47816f2fe186d0ba27567e7583d..874bd709c10296162355a061a341b3dcc3a5a5d5 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1636,
 /**/
     1635,
 /**/