summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-06-01 17:13:36 +0200
committerBram Moolenaar <Bram@vim.org>2019-06-01 17:13:36 +0200
commitbf0eff0b724ebf4951f7ca82e6c648451f9f0c01 (patch)
tree3be6478692b535abb96b8fe3963137e15581b206
parent2d247849ce612050ba1085df806746b23be1f0a3 (diff)
downloadvim-git-bf0eff0b724ebf4951f7ca82e6c648451f9f0c01.tar.gz
patch 8.1.1441: popup window filter not yet implementedv8.1.1441
Problem: Popup window filter not yet implemented. Solution: Implement the popup filter.
-rw-r--r--runtime/doc/popup.txt36
-rw-r--r--src/getchar.c4
-rw-r--r--src/misc2.c20
-rw-r--r--src/popupwin.c123
-rw-r--r--src/proto/misc2.pro1
-rw-r--r--src/proto/popupwin.pro3
-rw-r--r--src/screen.c45
-rw-r--r--src/structs.h1
-rw-r--r--src/testdir/test_popupwin.vim43
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h2
-rw-r--r--src/window.c3
12 files changed, 227 insertions, 56 deletions
diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt
index 02d037adb..1a4a9143d 100644
--- a/runtime/doc/popup.txt
+++ b/runtime/doc/popup.txt
@@ -1,4 +1,4 @@
-*popup.txt* For Vim version 8.1. Last change: 2019 May 31
+*popup.txt* For Vim version 8.1. Last change: 2019 Jun 01
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -90,11 +90,11 @@ Probably 2. is the best choice.
IMPLEMENTATION:
- Code is in popupwin.c
-- Implement filter.
- Check that popup_close() works in the filter.
+- Invoke filter with character before mapping?
+- Handle screen resize in screenalloc(). (Ben Jackson, #4467)
+- Why does 'nrformats' leak from the popup window buffer???
- Implement padding
- Implement border
-- Handle screen resize in screenalloc().
- Make redrawing more efficient and avoid flicker.
Store popup info in a mask, use the mask in screen_line()
Keep mask until next update_screen(), find differences and redraw affected
@@ -102,8 +102,8 @@ IMPLEMENTATION:
Fix redrawing problem with completion.
Fix redrawing problem when scrolling non-current window
Fix redrawing the statusline on top of a popup
-- Disable commands, feedkeys(), CTRL-W, etc. in a popup window. Or whitelist
- commands that are allowed?
+- Disable commands, feedkeys(), CTRL-W, etc. in a popup window.
+ Use NOT_IN_POPUP_WINDOW.
- Figure out the size and position better.
if wrapping splits a double-wide character
if wrapping inserts indent
@@ -385,7 +385,6 @@ The second argument of |popup_create()| is a dictionary with options:
{not implemented yet}
filter a callback that can filter typed characters, see
|popup-filter|
- {not implemented yet}
callback a callback to be used when the popup closes, e.g. when
using |popup_filter_menu()|, see |popup-callback|.
{not implemented yet}
@@ -426,7 +425,6 @@ So we get:
POPUP FILTER *popup-filter*
-{not implemented yet}
A callback that gets any typed keys while a popup is displayed. The filter is
not invoked when the popup is hidden.
@@ -437,10 +435,23 @@ filter is also called. The filter of the popup window with the highest zindex
is called first.
The filter function is called with two arguments: the ID of the popup and the
-key.
+key, e.g.: >
+ func MyFilter(winid, key)
+ if a:key == "\<F2>"
+ " do something
+ return 1
+ endif
+ if a:key == 'x'
+ call popup_close(a:winid)
+ return 1
+ endif
+ return 0
+ endfunc
+
+Currently the key is what results after any mapping. This may change...
Some common key actions:
- Esc close the popup
+ x close the popup (see note below)
cursor keys select another entry
Tab accept current suggestion
@@ -451,6 +462,11 @@ popup is col 1, row 1 (not counting the border).
Vim provides standard filters |popup_filter_menu()| and
|popup_filter_yesno()|.
+Note that "x" is the normal way to close a popup. You may want to use Esc,
+but since many keys start with an Esc character, there may be a delay before
+Vim recognizes the Esc key. If you do use Esc, it is reecommended to set the
+'ttimeoutlen' option to 100 and set 'timeout' and/or 'ttimeout'.
+
POPUP CALLBACK *popup-callback*
diff --git a/src/getchar.c b/src/getchar.c
index 02535ce20..80e98ec60 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -1801,6 +1801,10 @@ vgetc(void)
ui_remove_balloon();
}
#endif
+#ifdef FEAT_TEXT_PROP
+ if (popup_do_filter(c))
+ c = K_IGNORE;
+#endif
return c;
}
diff --git a/src/misc2.c b/src/misc2.c
index 69b9347bc..2ac7f5e77 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -2731,17 +2731,31 @@ get_special_key_name(int c, int modifiers)
trans_special(
char_u **srcp,
char_u *dst,
- int keycode, /* prefer key code, e.g. K_DEL instead of DEL */
- int in_string) /* TRUE when inside a double quoted string */
+ int keycode, // prefer key code, e.g. K_DEL instead of DEL
+ int in_string) // TRUE when inside a double quoted string
{
int modifiers = 0;
int key;
- int dlen = 0;
key = find_special_key(srcp, &modifiers, keycode, FALSE, in_string);
if (key == 0)
return 0;
+ return special_to_buf(key, modifiers, keycode, dst);
+}
+
+/*
+ * Put the character sequence for "key" with "modifiers" into "dst" and return
+ * the resulting length.
+ * When "keycode" is TRUE prefer key code, e.g. K_DEL instead of DEL.
+ * The sequence is not NUL terminated.
+ * This is how characters in a string are encoded.
+ */
+ int
+special_to_buf(int key, int modifiers, int keycode, char_u *dst)
+{
+ int dlen = 0;
+
/* Put the appropriate modifier in a string */
if (modifiers != 0)
{
diff --git a/src/popupwin.c b/src/popupwin.c
index 2ebfda2d9..620ef0506 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -149,25 +149,33 @@ apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict, int atcursor)
if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
{
wp->w_popup_timer = create_timer(nr, 0);
- wp->w_popup_timer->tr_callback.cb_name =
- vim_strsave(partial_name(tv.vval.v_partial));
- func_ref(wp->w_popup_timer->tr_callback.cb_name);
- wp->w_popup_timer->tr_callback.cb_partial = tv.vval.v_partial;
+ wp->w_popup_timer->tr_callback = get_callback(&tv);
+ clear_tv(&tv);
}
}
#endif
// Option values resulting in setting an option.
- str = dict_get_string(dict, (char_u *)"highlight", TRUE);
+ str = dict_get_string(dict, (char_u *)"highlight", FALSE);
if (str != NULL)
set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
str, OPT_FREE|OPT_LOCAL, 0);
+
di = dict_find(dict, (char_u *)"wrap", -1);
if (di != NULL)
{
nr = dict_get_number(dict, (char_u *)"wrap");
wp->w_p_wrap = nr != 0;
}
+
+ di = dict_find(dict, (char_u *)"filter", -1);
+ if (di != NULL)
+ {
+ callback_T callback = get_callback(&di->di_tv);
+
+ if (callback.cb_name != NULL)
+ set_callback(&wp->w_filter_cb, &callback);
+ }
}
/*
@@ -759,4 +767,109 @@ not_in_popup_window()
return FALSE;
}
+/*
+ * Reset all the POPF_HANDLED flags in global popup windows and popup windows
+ * in the current tab.
+ */
+ void
+popup_reset_handled()
+{
+ win_T *wp;
+
+ for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+ wp->w_popup_flags &= ~POPF_HANDLED;
+ for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
+ wp->w_popup_flags &= ~POPF_HANDLED;
+}
+
+/*
+ * Find the next visible popup where POPF_HANDLED is not set.
+ * Must have called popup_reset_handled() first.
+ * When "lowest" is TRUE find the popup with the lowest zindex, otherwise the
+ * popup with the highest zindex.
+ */
+ win_T *
+find_next_popup(int lowest)
+{
+ win_T *wp;
+ win_T *found_wp;
+ int found_zindex;
+
+ found_zindex = lowest ? INT_MAX : 0;
+ found_wp = NULL;
+ for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
+ if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
+ && (lowest ? wp->w_zindex < found_zindex
+ : wp->w_zindex > found_zindex))
+ {
+ found_zindex = wp->w_zindex;
+ found_wp = wp;
+ }
+ for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
+ if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
+ && (lowest ? wp->w_zindex < found_zindex
+ : wp->w_zindex > found_zindex))
+ {
+ found_zindex = wp->w_zindex;
+ found_wp = wp;
+ }
+
+ if (found_wp != NULL)
+ found_wp->w_popup_flags |= POPF_HANDLED;
+ return found_wp;
+}
+
+/*
+ * Invoke the filter callback for window "wp" with typed character "c".
+ * Uses the global "mod_mask" for modifiers.
+ * Returns the return value of the filter.
+ * Careful: The filter may make "wp" invalid!
+ */
+ static int
+invoke_popup_filter(win_T *wp, int c)
+{
+ int res;
+ typval_T rettv;
+ int dummy;
+ typval_T argv[3];
+ char_u buf[NUMBUFLEN];
+
+ argv[0].v_type = VAR_NUMBER;
+ argv[0].vval.v_number = (varnumber_T)wp->w_id;
+
+ // Convert the number to a string, so that the function can use:
+ // if a:c == "\<F2>"
+ buf[special_to_buf(c, mod_mask, TRUE, buf)] = NUL;
+ argv[1].v_type = VAR_STRING;
+ argv[1].vval.v_string = vim_strsave(buf);
+
+ argv[2].v_type = VAR_UNKNOWN;
+
+ call_callback(&wp->w_filter_cb, -1,
+ &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
+ res = tv_get_number(&rettv);
+ vim_free(argv[1].vval.v_string);
+ clear_tv(&rettv);
+ return res;
+}
+
+/*
+ * Called when "c" was typed: invoke popup filter callbacks.
+ * Returns TRUE when the character was consumed,
+ */
+ int
+popup_do_filter(int c)
+{
+ int res = FALSE;
+ win_T *wp;
+
+ popup_reset_handled();
+
+ while (!res && (wp = find_next_popup(FALSE)) != NULL)
+ if (wp->w_filter_cb.cb_name != NULL)
+ res = invoke_popup_filter(wp, c);
+
+ return res;
+}
+
#endif // FEAT_TEXT_PROP
diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro
index 84ed66a57..43921ed3a 100644
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -69,6 +69,7 @@ int simplify_key(int key, int *modifiers);
int handle_x_keys(int key);
char_u *get_special_key_name(int c, int modifiers);
int trans_special(char_u **srcp, char_u *dst, int keycode, int in_string);
+int special_to_buf(int key, int modifiers, int keycode, char_u *dst);
int find_special_key(char_u **srcp, int *modp, int keycode, int keep_x_key, int in_string);
int extract_modifiers(int key, int *modp);
int find_special_key_in_table(int c);
diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro
index 107a4273c..24a102395 100644
--- a/src/proto/popupwin.pro
+++ b/src/proto/popupwin.pro
@@ -14,4 +14,7 @@ void f_popup_move(typval_T *argvars, typval_T *rettv);
void f_popup_getpos(typval_T *argvars, typval_T *rettv);
void f_popup_getoptions(typval_T *argvars, typval_T *rettv);
int not_in_popup_window(void);
+void popup_reset_handled(void);
+win_T *find_next_popup(int lowest);
+int popup_do_filter(int c);
/* vim: set ft=c : */
diff --git a/src/screen.c b/src/screen.c
index 5f3947bf9..a8b10dedb 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -996,48 +996,19 @@ update_debug_sign(buf_T *buf, linenr_T lnum)
update_popups(void)
{
win_T *wp;
- win_T *lowest_wp;
- int lowest_zindex;
-
- // Reset all the VALID_POPUP flags.
- for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
- wp->w_popup_flags &= ~POPF_REDRAWN;
- for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
- wp->w_popup_flags &= ~POPF_REDRAWN;
+ // Find the window with the lowest zindex that hasn't been updated yet,
+ // so that the window with a higher zindex is drawn later, thus goes on
+ // top.
// TODO: don't redraw every popup every time.
- for (;;)
+ popup_reset_handled();
+ while ((wp = find_next_popup(TRUE)) != NULL)
{
- // Find the window with the lowest zindex that hasn't been updated yet,
- // so that the window with a higher zindex is drawn later, thus goes on
- // top.
- lowest_zindex = INT_MAX;
- lowest_wp = NULL;
- for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
- if ((wp->w_popup_flags & (POPF_REDRAWN|POPF_HIDDEN)) == 0
- && wp->w_zindex < lowest_zindex)
- {
- lowest_zindex = wp->w_zindex;
- lowest_wp = wp;
- }
- for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
- if ((wp->w_popup_flags & (POPF_REDRAWN|POPF_HIDDEN)) == 0
- && wp->w_zindex < lowest_zindex)
- {
- lowest_zindex = wp->w_zindex;
- lowest_wp = wp;
- }
-
- if (lowest_wp == NULL)
- break;
-
// Recompute the position if the text changed.
- if (lowest_wp->w_popup_last_changedtick
- != CHANGEDTICK(lowest_wp->w_buffer))
- popup_adjust_position(lowest_wp);
+ if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
+ popup_adjust_position(wp);
- win_update(lowest_wp);
- lowest_wp->w_popup_flags |= POPF_REDRAWN;
+ win_update(wp);
}
}
#endif
diff --git a/src/structs.h b/src/structs.h
index 2c164ca2b..9589b67db 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2890,6 +2890,7 @@ struct window_S
int w_wantcol; // "col" for popup window
varnumber_T w_popup_last_changedtick; // b:changedtick when position was
// computed
+ callback_T w_filter_cb; // popup filter callback
# if defined(FEAT_TIMERS)
timer_T *w_popup_timer; // timer for closing popup window
# endif
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 1c8f7ef6c..b9d6a062c 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -473,3 +473,46 @@ func Test_popup_atcursor()
bwipe!
endfunc
+
+func Test_popup_filter()
+ new
+ call setline(1, 'some text')
+
+ func MyPopupFilter(winid, c)
+ if a:c == 'e'
+ let g:eaten = 'e'
+ return 1
+ endif
+ if a:c == '0'
+ let g:ignored = '0'
+ return 0
+ endif
+ if a:c == 'x'
+ call popup_close(a:winid)
+ return 1
+ endif
+ return 0
+ endfunc
+
+ let winid = popup_create('something', {'filter': 'MyPopupFilter'})
+ redraw
+
+ " e is consumed by the filter
+ call feedkeys('e', 'xt')
+ call assert_equal('e', g:eaten)
+
+ " 0 is ignored by the filter
+ normal $
+ call assert_equal(9, getcurpos()[2])
+ call feedkeys('0', 'xt')
+ call assert_equal('0', g:ignored)
+ call assert_equal(1, getcurpos()[2])
+
+ " x closes the popup
+ call feedkeys('x', 'xt')
+ call assert_equal('e', g:eaten)
+ call assert_equal(-1, winbufnr(winid))
+
+ delfunc MyPopupFilter
+ popupclear
+endfunc
diff --git a/src/version.c b/src/version.c
index 96571af02..4b472ee89 100644
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1441,
+/**/
1440,
/**/
1439,
diff --git a/src/vim.h b/src/vim.h
index 378fe5f87..72bb109b4 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -615,7 +615,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
// Values for w_popup_flags.
#define POPF_HIDDEN 1 // popup is not displayed
-#define POPF_REDRAWN 2 // popup was just redrawn
+#define POPF_HANDLED 2 // popup was just redrawn or filtered
/*
* Terminal highlighting attribute bits.
diff --git a/src/window.c b/src/window.c
index 42689ee0b..b4ab11be3 100644
--- a/src/window.c
+++ b/src/window.c
@@ -4844,6 +4844,9 @@ win_free(
#ifdef FEAT_MENU
remove_winbar(wp);
#endif
+#ifdef FEAT_TEXT_PROP
+ free_callback(&wp->w_filter_cb);
+#endif
#ifdef FEAT_SYN_HL
vim_free(wp->w_p_cc_cols);