Skip to content
Permalink
Browse files

patch 8.1.1138: plugins don't get notified when the popup menu changes

Problem:    Plugins don't get notified when the popup menu changes.
Solution:   Add the CompleteChanged event. (Andy Massimino. closes #4176)
  • Loading branch information...
brammool committed Apr 8, 2019
1 parent 62e1bb4 commit d7f246c68cfb97406bcd4b098a2df2d870b3ef92
Showing with 164 additions and 17 deletions.
  1. +16 −0 runtime/doc/autocmd.txt
  2. +12 −0 src/autocmd.c
  3. +24 −4 src/dict.c
  4. +56 −13 src/insexpand.c
  5. +16 −0 src/popupmnu.c
  6. +1 −0 src/proto/autocmd.pro
  7. +1 −0 src/proto/dict.pro
  8. +1 −0 src/proto/popupmnu.pro
  9. +34 −0 src/testdir/test_popup.vim
  10. +2 −0 src/version.c
  11. +1 −0 src/vim.h
@@ -367,6 +367,7 @@ Name triggered by ~
|SessionLoadPost| after loading a session file

|MenuPopup| just before showing the popup menu
|CompleteChanged| after Insert mode completion menu changed
|CompleteDone| after Insert mode completion is done

|User| to be used in combination with ":doautocmd"
@@ -579,7 +580,22 @@ ColorScheme After loading a color scheme. |:colorscheme|
ColorSchemePre Before loading a color scheme. |:colorscheme|
Useful to setup removing things added by a
color scheme, before another one is loaded.
CompleteChanged *CompleteChanged*
After each time the Insert mode completion
menu changed. Not fired on popup menu hide,
use |CompleteDone| for that. Never triggered
recursively.

Sets these |v:event| keys:
completed_item
height nr of items visible
width screen cells
row top screen row
col leftmost screen column
size total nr of items
scrollbar TRUE if visible

It is not allowed to change the text |textlock|.
*CompleteDone*
CompleteDone After Insert mode completion is done. Either
when something was completed or abandoning
@@ -112,6 +112,7 @@ static struct event_name
{"CmdUndefined", EVENT_CMDUNDEFINED},
{"ColorScheme", EVENT_COLORSCHEME},
{"ColorSchemePre", EVENT_COLORSCHEMEPRE},
{"CompleteChanged", EVENT_COMPLETECHANGED},
{"CompleteDone", EVENT_COMPLETEDONE},
{"CursorHold", EVENT_CURSORHOLD},
{"CursorHoldI", EVENT_CURSORHOLDI},
@@ -1794,6 +1795,17 @@ has_textyankpost(void)
}
#endif

#if defined(FEAT_EVAL) || defined(PROTO)
/*
* Return TRUE when there is a CompleteChanged autocommand defined.
*/
int
has_completechanged(void)
{
return (first_autopat[(int)EVENT_COMPLETECHANGED] != NULL);
}
#endif

/*
* Execute autocommands for "event" and file name "fname".
* Return TRUE if some commands were executed.
@@ -342,18 +342,18 @@ dict_add(dict_T *d, dictitem_T *item)
}

/*
* Add a number entry to dictionary "d".
* Add a number or special entry to dictionary "d".
* Returns FAIL when out of memory and when key already exists.
*/
int
dict_add_number(dict_T *d, char *key, varnumber_T nr)
static int
dict_add_number_special(dict_T *d, char *key, varnumber_T nr, int special)
{
dictitem_T *item;

item = dictitem_alloc((char_u *)key);
if (item == NULL)
return FAIL;
item->di_tv.v_type = VAR_NUMBER;
item->di_tv.v_type = special ? VAR_SPECIAL : VAR_NUMBER;
item->di_tv.vval.v_number = nr;
if (dict_add(d, item) == FAIL)
{
@@ -363,6 +363,26 @@ dict_add_number(dict_T *d, char *key, varnumber_T nr)
return OK;
}

/*
* Add a number entry to dictionary "d".
* Returns FAIL when out of memory and when key already exists.
*/
int
dict_add_number(dict_T *d, char *key, varnumber_T nr)
{
return dict_add_number_special(d, key, nr, FALSE);
}

/*
* Add a special entry to dictionary "d".
* Returns FAIL when out of memory and when key already exists.
*/
int
dict_add_special(dict_T *d, char *key, varnumber_T nr)
{
return dict_add_number_special(d, key, nr, TRUE);
}

/*
* Add a string entry to dictionary "d".
* Returns FAIL when out of memory and when key already exists.
@@ -203,6 +203,7 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg);
static void ins_compl_add_list(list_T *list);
static void ins_compl_add_dict(dict_T *dict);
# endif
static dict_T *ins_compl_dict_alloc(compl_T *match);
static int ins_compl_key2dir(int c);
static int ins_compl_pum_key(int c);
static int ins_compl_key2count(int c);
@@ -994,6 +995,37 @@ pum_enough_matches(void)
return (i >= 2);
}

static void
trigger_complete_changed_event(int cur)
{
dict_T *v_event;
dict_T *item;
static int recursive = FALSE;

if (recursive)
return;

v_event = get_vim_var_dict(VV_EVENT);
if (cur < 0)
item = dict_alloc();
else
item = ins_compl_dict_alloc(compl_curr_match);
if (item == NULL)
return;
dict_add_dict(v_event, "completed_item", item);
pum_set_event_info(v_event);
dict_set_items_ro(v_event);

recursive = TRUE;
textlock++;
apply_autocmds(EVENT_COMPLETECHANGED, NULL, NULL, FALSE, curbuf);
textlock--;
recursive = FALSE;

dict_free_contents(v_event);
hash_init(&v_event->dv_hashtab);
}

/*
* Show the popup menu for the list of matches.
* Also adjusts "compl_shown_match" to an entry that is actually displayed.
@@ -1136,6 +1168,9 @@ ins_compl_show_pum(void)
curwin->w_cursor.col = compl_col;
pum_display(compl_match_array, compl_match_arraysize, cur);
curwin->w_cursor.col = col;

if (has_completechanged())
trigger_complete_changed_event(cur);
}
}

@@ -2899,23 +2934,31 @@ ins_compl_insert(int in_compl_func)
compl_used_match = FALSE;
else
compl_used_match = TRUE;
dict = ins_compl_dict_alloc(compl_shown_match);
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
if (!in_compl_func)
compl_curr_match = compl_shown_match;
}

/*
* Allocate Dict for the completed item.
* { word, abbr, menu, kind, info }
*/
static dict_T *
ins_compl_dict_alloc(compl_T *match)
{
dict_T *dict = dict_alloc_lock(VAR_FIXED);

// Set completed item.
// { word, abbr, menu, kind, info }
dict = dict_alloc_lock(VAR_FIXED);
if (dict != NULL)
{
dict_add_string(dict, "word", compl_shown_match->cp_str);
dict_add_string(dict, "abbr", compl_shown_match->cp_text[CPT_ABBR]);
dict_add_string(dict, "menu", compl_shown_match->cp_text[CPT_MENU]);
dict_add_string(dict, "kind", compl_shown_match->cp_text[CPT_KIND]);
dict_add_string(dict, "info", compl_shown_match->cp_text[CPT_INFO]);
dict_add_string(dict, "user_data",
compl_shown_match->cp_text[CPT_USER_DATA]);
dict_add_string(dict, "word", match->cp_str);
dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]);
dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
dict_add_string(dict, "info", match->cp_text[CPT_INFO]);
dict_add_string(dict, "user_data", match->cp_text[CPT_USER_DATA]);
}
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
if (!in_compl_func)
compl_curr_match = compl_shown_match;
return dict;
}

/*
@@ -923,6 +923,22 @@ pum_get_height(void)
return pum_height;
}

/*
* Add size information about the pum to "dict".
*/
void
pum_set_event_info(dict_T *dict)
{
if (!pum_visible())
return;
dict_add_number(dict, "height", pum_height);
dict_add_number(dict, "width", pum_width);
dict_add_number(dict, "row", pum_row);
dict_add_number(dict, "col", pum_col);
dict_add_number(dict, "size", pum_size);
dict_add_special(dict, "scrollbar", pum_scrollbar ? VVAL_TRUE : VVAL_FALSE);
}

# if defined(FEAT_BEVAL_TERM) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
static void
pum_position_at_mouse(int min_width)
@@ -26,6 +26,7 @@ int has_insertcharpre(void);
int has_cmdundefined(void);
int has_funcundefined(void);
int has_textyankpost(void);
int has_completechanged(void);
void block_autocmds(void);
void unblock_autocmds(void);
int is_autocmd_blocked(void);
@@ -14,6 +14,7 @@ void dictitem_free(dictitem_T *item);
dict_T *dict_copy(dict_T *orig, int deep, int copyID);
int dict_add(dict_T *d, dictitem_T *item);
int dict_add_number(dict_T *d, char *key, varnumber_T nr);
int dict_add_special(dict_T *d, char *key, varnumber_T nr);
int dict_add_string(dict_T *d, char *key, char_u *str);
int dict_add_string_len(dict_T *d, char *key, char_u *str, int len);
int dict_add_list(dict_T *d, char *key, list_T *list);
@@ -8,6 +8,7 @@ void pum_clear(void);
int pum_visible(void);
void pum_may_redraw(void);
int pum_get_height(void);
void pum_set_event_info(dict_T *dict);
int split_message(char_u *mesg, pumitem_T **array);
void ui_remove_balloon(void);
void ui_post_balloon(char_u *mesg, list_T *list);
@@ -1029,4 +1029,38 @@ func Test_popup_complete_info_02()
bwipe!
endfunc

func Test_CompleteChanged()
new
call setline(1, ['foo', 'bar', 'foobar', ''])
set complete=. completeopt=noinsert,noselect,menuone
function! OnPumChange()
let g:event = copy(v:event)
let g:item = get(v:event, 'completed_item', {})
let g:word = get(g:item, 'word', v:null)
endfunction
augroup AAAAA_Group
au!
autocmd CompleteChanged * :call OnPumChange()
augroup END
call cursor(4, 1)

call feedkeys("Sf\<C-N>", 'tx')
call assert_equal({'completed_item': {}, 'width': 15,
\ 'height': 2, 'size': 2,
\ 'col': 0, 'row': 4, 'scrollbar': v:false}, g:event)
call feedkeys("a\<C-N>\<C-N>\<C-E>", 'tx')
call assert_equal('foo', g:word)
call feedkeys("a\<C-N>\<C-N>\<C-N>\<C-E>", 'tx')
call assert_equal('foobar', g:word)
call feedkeys("a\<C-N>\<C-N>\<C-N>\<C-N>\<C-E>", 'tx')
call assert_equal(v:null, g:word)
call feedkeys("a\<C-N>\<C-N>\<C-N>\<C-N>\<C-P>", 'tx')
call assert_equal('foobar', g:word)

autocmd! AAAAA_Group
set complete& completeopt&
delfunc! OnPumchange
bw!
endfunc

" vim: shiftwidth=2 sts=2 expandtab
@@ -771,6 +771,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1138,
/**/
1137,
/**/
@@ -1270,6 +1270,7 @@ enum auto_event
EVENT_CMDWINLEAVE, // before leaving the cmdline window
EVENT_COLORSCHEME, // after loading a colorscheme
EVENT_COLORSCHEMEPRE, // before loading a colorscheme
EVENT_COMPLETECHANGED, // after completion popup menu changed
EVENT_COMPLETEDONE, // after finishing insert complete
EVENT_CURSORHOLD, // cursor in same position for a while
EVENT_CURSORHOLDI, // idem, in Insert mode

0 comments on commit d7f246c

Please sign in to comment.
You can’t perform that action at this time.