Skip to content

Commit bf0eff0

Browse files
committed
patch 8.1.1441: popup window filter not yet implemented
Problem: Popup window filter not yet implemented. Solution: Implement the popup filter.
1 parent 2d24784 commit bf0eff0

12 files changed

Lines changed: 227 additions & 56 deletions

File tree

runtime/doc/popup.txt

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*popup.txt* For Vim version 8.1. Last change: 2019 May 31
1+
*popup.txt* For Vim version 8.1. Last change: 2019 Jun 01
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -90,20 +90,20 @@ Probably 2. is the best choice.
9090

9191
IMPLEMENTATION:
9292
- Code is in popupwin.c
93-
- Implement filter.
94-
Check that popup_close() works in the filter.
93+
- Invoke filter with character before mapping?
94+
- Handle screen resize in screenalloc(). (Ben Jackson, #4467)
95+
- Why does 'nrformats' leak from the popup window buffer???
9596
- Implement padding
9697
- Implement border
97-
- Handle screen resize in screenalloc().
9898
- Make redrawing more efficient and avoid flicker.
9999
Store popup info in a mask, use the mask in screen_line()
100100
Keep mask until next update_screen(), find differences and redraw affected
101101
windows/lines
102102
Fix redrawing problem with completion.
103103
Fix redrawing problem when scrolling non-current window
104104
Fix redrawing the statusline on top of a popup
105-
- Disable commands, feedkeys(), CTRL-W, etc. in a popup window. Or whitelist
106-
commands that are allowed?
105+
- Disable commands, feedkeys(), CTRL-W, etc. in a popup window.
106+
Use NOT_IN_POPUP_WINDOW.
107107
- Figure out the size and position better.
108108
if wrapping splits a double-wide character
109109
if wrapping inserts indent
@@ -385,7 +385,6 @@ The second argument of |popup_create()| is a dictionary with options:
385385
{not implemented yet}
386386
filter a callback that can filter typed characters, see
387387
|popup-filter|
388-
{not implemented yet}
389388
callback a callback to be used when the popup closes, e.g. when
390389
using |popup_filter_menu()|, see |popup-callback|.
391390
{not implemented yet}
@@ -426,7 +425,6 @@ So we get:
426425

427426
POPUP FILTER *popup-filter*
428427

429-
{not implemented yet}
430428
A callback that gets any typed keys while a popup is displayed. The filter is
431429
not invoked when the popup is hidden.
432430

@@ -437,10 +435,23 @@ filter is also called. The filter of the popup window with the highest zindex
437435
is called first.
438436

439437
The filter function is called with two arguments: the ID of the popup and the
440-
key.
438+
key, e.g.: >
439+
func MyFilter(winid, key)
440+
if a:key == "\<F2>"
441+
" do something
442+
return 1
443+
endif
444+
if a:key == 'x'
445+
call popup_close(a:winid)
446+
return 1
447+
endif
448+
return 0
449+
endfunc
450+
451+
Currently the key is what results after any mapping. This may change...
441452

442453
Some common key actions:
443-
Esc close the popup
454+
x close the popup (see note below)
444455
cursor keys select another entry
445456
Tab accept current suggestion
446457

@@ -451,6 +462,11 @@ popup is col 1, row 1 (not counting the border).
451462
Vim provides standard filters |popup_filter_menu()| and
452463
|popup_filter_yesno()|.
453464

465+
Note that "x" is the normal way to close a popup. You may want to use Esc,
466+
but since many keys start with an Esc character, there may be a delay before
467+
Vim recognizes the Esc key. If you do use Esc, it is reecommended to set the
468+
'ttimeoutlen' option to 100 and set 'timeout' and/or 'ttimeout'.
469+
454470

455471
POPUP CALLBACK *popup-callback*
456472

src/getchar.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,10 @@ vgetc(void)
18011801
ui_remove_balloon();
18021802
}
18031803
#endif
1804+
#ifdef FEAT_TEXT_PROP
1805+
if (popup_do_filter(c))
1806+
c = K_IGNORE;
1807+
#endif
18041808

18051809
return c;
18061810
}

src/misc2.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2731,17 +2731,31 @@ get_special_key_name(int c, int modifiers)
27312731
trans_special(
27322732
char_u **srcp,
27332733
char_u *dst,
2734-
int keycode, /* prefer key code, e.g. K_DEL instead of DEL */
2735-
int in_string) /* TRUE when inside a double quoted string */
2734+
int keycode, // prefer key code, e.g. K_DEL instead of DEL
2735+
int in_string) // TRUE when inside a double quoted string
27362736
{
27372737
int modifiers = 0;
27382738
int key;
2739-
int dlen = 0;
27402739

27412740
key = find_special_key(srcp, &modifiers, keycode, FALSE, in_string);
27422741
if (key == 0)
27432742
return 0;
27442743

2744+
return special_to_buf(key, modifiers, keycode, dst);
2745+
}
2746+
2747+
/*
2748+
* Put the character sequence for "key" with "modifiers" into "dst" and return
2749+
* the resulting length.
2750+
* When "keycode" is TRUE prefer key code, e.g. K_DEL instead of DEL.
2751+
* The sequence is not NUL terminated.
2752+
* This is how characters in a string are encoded.
2753+
*/
2754+
int
2755+
special_to_buf(int key, int modifiers, int keycode, char_u *dst)
2756+
{
2757+
int dlen = 0;
2758+
27452759
/* Put the appropriate modifier in a string */
27462760
if (modifiers != 0)
27472761
{

src/popupwin.c

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,25 +149,33 @@ apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict, int atcursor)
149149
if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
150150
{
151151
wp->w_popup_timer = create_timer(nr, 0);
152-
wp->w_popup_timer->tr_callback.cb_name =
153-
vim_strsave(partial_name(tv.vval.v_partial));
154-
func_ref(wp->w_popup_timer->tr_callback.cb_name);
155-
wp->w_popup_timer->tr_callback.cb_partial = tv.vval.v_partial;
152+
wp->w_popup_timer->tr_callback = get_callback(&tv);
153+
clear_tv(&tv);
156154
}
157155
}
158156
#endif
159157

160158
// Option values resulting in setting an option.
161-
str = dict_get_string(dict, (char_u *)"highlight", TRUE);
159+
str = dict_get_string(dict, (char_u *)"highlight", FALSE);
162160
if (str != NULL)
163161
set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
164162
str, OPT_FREE|OPT_LOCAL, 0);
163+
165164
di = dict_find(dict, (char_u *)"wrap", -1);
166165
if (di != NULL)
167166
{
168167
nr = dict_get_number(dict, (char_u *)"wrap");
169168
wp->w_p_wrap = nr != 0;
170169
}
170+
171+
di = dict_find(dict, (char_u *)"filter", -1);
172+
if (di != NULL)
173+
{
174+
callback_T callback = get_callback(&di->di_tv);
175+
176+
if (callback.cb_name != NULL)
177+
set_callback(&wp->w_filter_cb, &callback);
178+
}
171179
}
172180

173181
/*
@@ -759,4 +767,109 @@ not_in_popup_window()
759767
return FALSE;
760768
}
761769

770+
/*
771+
* Reset all the POPF_HANDLED flags in global popup windows and popup windows
772+
* in the current tab.
773+
*/
774+
void
775+
popup_reset_handled()
776+
{
777+
win_T *wp;
778+
779+
for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
780+
wp->w_popup_flags &= ~POPF_HANDLED;
781+
for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
782+
wp->w_popup_flags &= ~POPF_HANDLED;
783+
}
784+
785+
/*
786+
* Find the next visible popup where POPF_HANDLED is not set.
787+
* Must have called popup_reset_handled() first.
788+
* When "lowest" is TRUE find the popup with the lowest zindex, otherwise the
789+
* popup with the highest zindex.
790+
*/
791+
win_T *
792+
find_next_popup(int lowest)
793+
{
794+
win_T *wp;
795+
win_T *found_wp;
796+
int found_zindex;
797+
798+
found_zindex = lowest ? INT_MAX : 0;
799+
found_wp = NULL;
800+
for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
801+
if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
802+
&& (lowest ? wp->w_zindex < found_zindex
803+
: wp->w_zindex > found_zindex))
804+
{
805+
found_zindex = wp->w_zindex;
806+
found_wp = wp;
807+
}
808+
for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
809+
if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
810+
&& (lowest ? wp->w_zindex < found_zindex
811+
: wp->w_zindex > found_zindex))
812+
{
813+
found_zindex = wp->w_zindex;
814+
found_wp = wp;
815+
}
816+
817+
if (found_wp != NULL)
818+
found_wp->w_popup_flags |= POPF_HANDLED;
819+
return found_wp;
820+
}
821+
822+
/*
823+
* Invoke the filter callback for window "wp" with typed character "c".
824+
* Uses the global "mod_mask" for modifiers.
825+
* Returns the return value of the filter.
826+
* Careful: The filter may make "wp" invalid!
827+
*/
828+
static int
829+
invoke_popup_filter(win_T *wp, int c)
830+
{
831+
int res;
832+
typval_T rettv;
833+
int dummy;
834+
typval_T argv[3];
835+
char_u buf[NUMBUFLEN];
836+
837+
argv[0].v_type = VAR_NUMBER;
838+
argv[0].vval.v_number = (varnumber_T)wp->w_id;
839+
840+
// Convert the number to a string, so that the function can use:
841+
// if a:c == "\<F2>"
842+
buf[special_to_buf(c, mod_mask, TRUE, buf)] = NUL;
843+
argv[1].v_type = VAR_STRING;
844+
argv[1].vval.v_string = vim_strsave(buf);
845+
846+
argv[2].v_type = VAR_UNKNOWN;
847+
848+
call_callback(&wp->w_filter_cb, -1,
849+
&rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
850+
res = tv_get_number(&rettv);
851+
vim_free(argv[1].vval.v_string);
852+
clear_tv(&rettv);
853+
return res;
854+
}
855+
856+
/*
857+
* Called when "c" was typed: invoke popup filter callbacks.
858+
* Returns TRUE when the character was consumed,
859+
*/
860+
int
861+
popup_do_filter(int c)
862+
{
863+
int res = FALSE;
864+
win_T *wp;
865+
866+
popup_reset_handled();
867+
868+
while (!res && (wp = find_next_popup(FALSE)) != NULL)
869+
if (wp->w_filter_cb.cb_name != NULL)
870+
res = invoke_popup_filter(wp, c);
871+
872+
return res;
873+
}
874+
762875
#endif // FEAT_TEXT_PROP

src/proto/misc2.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ int simplify_key(int key, int *modifiers);
6969
int handle_x_keys(int key);
7070
char_u *get_special_key_name(int c, int modifiers);
7171
int trans_special(char_u **srcp, char_u *dst, int keycode, int in_string);
72+
int special_to_buf(int key, int modifiers, int keycode, char_u *dst);
7273
int find_special_key(char_u **srcp, int *modp, int keycode, int keep_x_key, int in_string);
7374
int extract_modifiers(int key, int *modp);
7475
int find_special_key_in_table(int c);

src/proto/popupwin.pro

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ void f_popup_move(typval_T *argvars, typval_T *rettv);
1414
void f_popup_getpos(typval_T *argvars, typval_T *rettv);
1515
void f_popup_getoptions(typval_T *argvars, typval_T *rettv);
1616
int not_in_popup_window(void);
17+
void popup_reset_handled(void);
18+
win_T *find_next_popup(int lowest);
19+
int popup_do_filter(int c);
1720
/* vim: set ft=c : */

src/screen.c

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -996,48 +996,19 @@ update_debug_sign(buf_T *buf, linenr_T lnum)
996996
update_popups(void)
997997
{
998998
win_T *wp;
999-
win_T *lowest_wp;
1000-
int lowest_zindex;
1001-
1002-
// Reset all the VALID_POPUP flags.
1003-
for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
1004-
wp->w_popup_flags &= ~POPF_REDRAWN;
1005-
for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
1006-
wp->w_popup_flags &= ~POPF_REDRAWN;
1007999

1000+
// Find the window with the lowest zindex that hasn't been updated yet,
1001+
// so that the window with a higher zindex is drawn later, thus goes on
1002+
// top.
10081003
// TODO: don't redraw every popup every time.
1009-
for (;;)
1004+
popup_reset_handled();
1005+
while ((wp = find_next_popup(TRUE)) != NULL)
10101006
{
1011-
// Find the window with the lowest zindex that hasn't been updated yet,
1012-
// so that the window with a higher zindex is drawn later, thus goes on
1013-
// top.
1014-
lowest_zindex = INT_MAX;
1015-
lowest_wp = NULL;
1016-
for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
1017-
if ((wp->w_popup_flags & (POPF_REDRAWN|POPF_HIDDEN)) == 0
1018-
&& wp->w_zindex < lowest_zindex)
1019-
{
1020-
lowest_zindex = wp->w_zindex;
1021-
lowest_wp = wp;
1022-
}
1023-
for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
1024-
if ((wp->w_popup_flags & (POPF_REDRAWN|POPF_HIDDEN)) == 0
1025-
&& wp->w_zindex < lowest_zindex)
1026-
{
1027-
lowest_zindex = wp->w_zindex;
1028-
lowest_wp = wp;
1029-
}
1030-
1031-
if (lowest_wp == NULL)
1032-
break;
1033-
10341007
// Recompute the position if the text changed.
1035-
if (lowest_wp->w_popup_last_changedtick
1036-
!= CHANGEDTICK(lowest_wp->w_buffer))
1037-
popup_adjust_position(lowest_wp);
1008+
if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
1009+
popup_adjust_position(wp);
10381010

1039-
win_update(lowest_wp);
1040-
lowest_wp->w_popup_flags |= POPF_REDRAWN;
1011+
win_update(wp);
10411012
}
10421013
}
10431014
#endif

src/structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2890,6 +2890,7 @@ struct window_S
28902890
int w_wantcol; // "col" for popup window
28912891
varnumber_T w_popup_last_changedtick; // b:changedtick when position was
28922892
// computed
2893+
callback_T w_filter_cb; // popup filter callback
28932894
# if defined(FEAT_TIMERS)
28942895
timer_T *w_popup_timer; // timer for closing popup window
28952896
# endif

0 commit comments

Comments
 (0)