1
0
forked from aniani/vim

patch 8.1.1441: popup window filter not yet implemented

Problem:    Popup window filter not yet implemented.
Solution:   Implement the popup filter.
This commit is contained in:
Bram Moolenaar 2019-06-01 17:13:36 +02:00
parent 2d247849ce
commit bf0eff0b72
12 changed files with 227 additions and 56 deletions

View File

@ -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*

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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

View File

@ -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);

View File

@ -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 : */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -767,6 +767,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1441,
/**/
1440,
/**/

View File

@ -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.

View File

@ -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);