# HG changeset patch # User Bram Moolenaar # Date 1554741006 -7200 # Node ID 0f65f28084704d3957b1f8155838bbf6ed9d1189 # Parent b471858040bc119bdb0c441dcf8b1b1071c4697c patch 8.1.1138: plugins don't get notified when the popup menu changes commit https://github.com/vim/vim/commit/d7f246c68cfb97406bcd4b098a2df2d870b3ef92 Author: Bram Moolenaar Date: Mon Apr 8 18:15:41 2019 +0200 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 https://github.com/vim/vim/issues/4176) diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -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 sche 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 diff --git a/src/autocmd.c b/src/autocmd.c --- a/src/autocmd.c +++ b/src/autocmd.c @@ -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. diff --git a/src/dict.c b/src/dict.c --- a/src/dict.c +++ b/src/dict.c @@ -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) { @@ -364,6 +364,26 @@ dict_add_number(dict_T *d, char *key, va } /* + * 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. */ diff --git a/src/insexpand.c b/src/insexpand.c --- a/src/insexpand.c +++ b/src/insexpand.c @@ -203,6 +203,7 @@ static void ins_compl_fixRedoBufForLeade 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,26 +2934,34 @@ ins_compl_insert(int in_compl_func) compl_used_match = FALSE; else compl_used_match = TRUE; - - // 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 = 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); + + if (dict != NULL) + { + 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]); + } + return dict; +} + +/* * Fill in the next completion in the current direction. * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to * get more completions. If it is FALSE, then we just do nothing when there diff --git a/src/popupmnu.c b/src/popupmnu.c --- a/src/popupmnu.c +++ b/src/popupmnu.c @@ -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) diff --git a/src/proto/autocmd.pro b/src/proto/autocmd.pro --- a/src/proto/autocmd.pro +++ b/src/proto/autocmd.pro @@ -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); diff --git a/src/proto/dict.pro b/src/proto/dict.pro --- a/src/proto/dict.pro +++ b/src/proto/dict.pro @@ -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); diff --git a/src/proto/popupmnu.pro b/src/proto/popupmnu.pro --- a/src/proto/popupmnu.pro +++ b/src/proto/popupmnu.pro @@ -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); diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -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\", 'tx') + call assert_equal({'completed_item': {}, 'width': 15, + \ 'height': 2, 'size': 2, + \ 'col': 0, 'row': 4, 'scrollbar': v:false}, g:event) + call feedkeys("a\\\", 'tx') + call assert_equal('foo', g:word) + call feedkeys("a\\\\", 'tx') + call assert_equal('foobar', g:word) + call feedkeys("a\\\\\", 'tx') + call assert_equal(v:null, g:word) + call feedkeys("a\\\\\", 'tx') + call assert_equal('foobar', g:word) + + autocmd! AAAAA_Group + set complete& completeopt& + delfunc! OnPumchange + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -772,6 +772,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1138, +/**/ 1137, /**/ 1136, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -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