# HG changeset patch # User Bram Moolenaar # Date 1553859006 -3600 # Node ID 0375e54f0adc5fa93eff5098b15d430df73ef43b # Parent b792e47f7a2d249c6b1b853b6642e84e02d889e5 patch 8.1.1068: cannot get all the information about current completion commit https://github.com/vim/vim/commit/fd133323d4e1cc9c0e61c0ce357df4d36ea148e3 Author: Bram Moolenaar Date: Fri Mar 29 12:20:27 2019 +0100 patch 8.1.1068: cannot get all the information about current completion Problem: Cannot get all the information about current completion. Solution: Add complete_info(). (Shougo, Hirohito Higashi, closes https://github.com/vim/vim/issues/4106) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.1. Last change: 2019 Mar 23 +*eval.txt* For Vim version 8.1. Last change: 2019 Mar 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2267,6 +2267,7 @@ col({expr}) Number column nr of cursor complete({startcol}, {matches}) none set Insert mode completion complete_add({expr}) Number add completion match complete_check() Number check for key typed during completion +complete_info([{what}]) Dict get current completion information confirm({msg} [, {choices} [, {default} [, {type}]]]) Number number of choice picked by user copy({expr}) any make a shallow copy of {expr} @@ -3452,8 +3453,8 @@ cindent({lnum}) *cindent()* See |C-indenting|. clearmatches() *clearmatches()* - Clears all matches previously defined by |matchadd()| and the - |:match| commands. + Clears all matches previously defined for the current window + by |matchadd()| and the |:match| commands. *col()* col({expr}) The result is a Number, which is the byte index of the column @@ -3538,6 +3539,55 @@ complete_check() *complete_check()* Only to be used by the function specified with the 'completefunc' option. + *complete_info()* +complete_info([{what}]) + Returns a Dictionary with information about Insert mode + completion. See |ins-completion|. + The items are: + mode Current completion mode name string. + See |completion_info_mode| for the values. + pum_visible |TRUE| if popup menu is visible. + See |pumvisible()|. + items List of completion matches. Each item is a + dictionary containing the entries "word", + "abbr", "menu", "kind", "info" and "user_data". + See |complete-items|. + selected Selected item index. First index is zero. + Index is -1 if no item is selected (showing + typed text only) + inserted Inserted string. [NOT IMPLEMENT YET] + + *complete_info_mode* + mode values are: + "" Not in completion mode + "keyword" Keyword completion |i_CTRL-X_CTRL-N| + "ctrl_x" Just pressed CTRL-X |i_CTRL-X| + "whole_line" Whole lines |i_CTRL-X_CTRL-L| + "files" File names |i_CTRL-X_CTRL-F| + "tags" Tags |i_CTRL-X_CTRL-]| + "path_defines" Definition completion |i_CTRL-X_CTRL-D| + "path_patterns" Include completion |i_CTRL-X_CTRL-I| + "dictionary" Dictionary |i_CTRL-X_CTRL-K| + "thesaurus" Thesaurus |i_CTRL-X_CTRL-T| + "cmdline" Vim Command line |i_CTRL-X_CTRL-V| + "function" User defined completion |i_CTRL-X_CTRL-U| + "omni" Omni completion |i_CTRL-X_CTRL-O| + "spell" Spelling suggestions |i_CTRL-X_s| + "eval" |complete()| completion + "unknown" Other internal modes + + If the optional {what} list argument is supplied, then only + the items listed in {what} are returned. Unsupported items in + {what} are silently ignored. + + Examples: > + " Get all items + call complete_info() + " Get only 'mode' + call complete_info(['mode']) + " Get only 'mode' and 'pum_visible' + call complete_info(['mode', 'pum_visible']) +< *confirm()* confirm({msg} [, {choices} [, {default} [, {type}]]]) confirm() offers the user a dialog, from which a choice can be @@ -4978,10 +5028,11 @@ getloclist({nr} [, {what}]) *getlocli details. getmatches() *getmatches()* - Returns a |List| with all matches previously defined by - |matchadd()| and the |:match| commands. |getmatches()| is - useful in combination with |setmatches()|, as |setmatches()| - can restore a list of matches saved by |getmatches()|. + Returns a |List| with all matches previously defined for the + current window by |matchadd()| and the |:match| commands. + |getmatches()| is useful in combination with |setmatches()|, + as |setmatches()| can restore a list of matches saved by + |getmatches()|. Example: > :echo getmatches() < [{'group': 'MyGroup1', 'pattern': 'TODO', @@ -7839,9 +7890,10 @@ setloclist({nr}, {list} [, {action} [, { for the list of supported keys in {what}. setmatches({list}) *setmatches()* - Restores a list of matches saved by |getmatches()|. Returns 0 - if successful, otherwise -1. All current matches are cleared - before the list is restored. See example for |getmatches()|. + Restores a list of matches saved by |getmatches() for the + current window|. Returns 0 if successful, otherwise -1. All + current matches are cleared before the list is restored. See + example for |getmatches()|. *setpos()* setpos({expr}, {list}) diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1,4 +1,4 @@ -*insert.txt* For Vim version 8.1. Last change: 2019 Jan 29 +*insert.txt* For Vim version 8.1. Last change: 2019 Mar 26 VIM REFERENCE MANUAL by Bram Moolenaar @@ -379,8 +379,8 @@ CTRL-\ CTRL-O like CTRL-O but don't move CTRL-L when 'insertmode' is set: go to Normal mode *i_CTRL-L* CTRL-G u break undo sequence, start new change *i_CTRL-G_u* CTRL-G U don't break undo with next left/right cursor *i_CTRL-G_U* - movement (but only if the cursor stays - within same the line) + movement, if the cursor stays within + same the line ----------------------------------------------------------------------- Note: If the cursor keys take you out of Insert mode, check the 'noesckeys' @@ -642,6 +642,7 @@ and one of the CTRL-X commands. You exi not a valid CTRL-X mode command. Valid keys are the CTRL-X command itself, CTRL-N (next), and CTRL-P (previous). +To get the current completion information, |complete_info()| can be used. Also see the 'infercase' option if you want to adjust the case of the match. *complete_CTRL-E* diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -834,6 +834,7 @@ Insert mode completion: *completion-f complete() set found matches complete_add() add to found matches complete_check() check if completion should be aborted + complete_info() get current completion information pumvisible() check if the popup menu is displayed Folding: *folding-functions* diff --git a/src/edit.c b/src/edit.c --- a/src/edit.c +++ b/src/edit.c @@ -15,7 +15,9 @@ #ifdef FEAT_INS_EXPAND /* - * definitions used for CTRL-X submode + * Definitions used for CTRL-X submode. + * Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] and + * ctrl_x_mode_names[]. */ # define CTRL_X_WANT_IDENT 0x100 @@ -40,18 +42,18 @@ # define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] # define CTRL_X_MODE_LINE_OR_EVAL(m) ((m) == CTRL_X_WHOLE_LINE || (m) == CTRL_X_EVAL) -/* Message for CTRL-X mode, index is ctrl_x_mode. */ +// Message for CTRL-X mode, index is ctrl_x_mode. static char *ctrl_x_msgs[] = { - N_(" Keyword completion (^N^P)"), /* CTRL_X_NORMAL, ^P/^N compl. */ + N_(" Keyword completion (^N^P)"), // CTRL_X_NORMAL, ^P/^N compl. N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), - NULL, /* CTRL_X_SCROLL: depends on state */ + NULL, // CTRL_X_SCROLL: depends on state N_(" Whole line completion (^L^N^P)"), N_(" File name completion (^F^N^P)"), N_(" Tag completion (^]^N^P)"), N_(" Path pattern completion (^N^P)"), N_(" Definition completion (^D^N^P)"), - NULL, /* CTRL_X_FINISHED */ + NULL, // CTRL_X_FINISHED N_(" Dictionary completion (^K^N^P)"), N_(" Thesaurus completion (^T^N^P)"), N_(" Command-line completion (^V^N^P)"), @@ -59,9 +61,30 @@ static char *ctrl_x_msgs[] = N_(" Omni completion (^O^N^P)"), N_(" Spelling suggestion (s^N^P)"), N_(" Keyword Local completion (^N^P)"), - NULL, /* CTRL_X_EVAL doesn't use msg. */ + NULL, // CTRL_X_EVAL doesn't use msg. }; +static char *ctrl_x_mode_names[] = { + "keyword", + "ctrl_x", + "unknown", // CTRL_X_SCROLL + "whole_line", + "files", + "tags", + "path_patterns", + "path_defines", + "unknown", // CTRL_X_FINISHED + "dictionary", + "thesaurus", + "cmdline", + "function", + "omni", + "spell", + NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" + "eval" + }; + + static char e_hitend[] = N_("Hit end of paragraph"); # ifdef FEAT_COMPL_FUNC static char e_complwin[] = N_("E839: Completion function changed window"); @@ -163,6 +186,7 @@ static void ins_compl_files(int count, c static char_u *find_line_end(char_u *ptr); static void ins_compl_free(void); static void ins_compl_clear(void); +static char_u *ins_compl_mode(void); static int ins_compl_bs(void); static int ins_compl_need_restart(void); static void ins_compl_new_leader(void); @@ -3525,6 +3549,108 @@ ins_compl_active(void) return compl_started; } + +/* + * Get complete information + */ + void +get_complete_info(list_T *what_list, dict_T *retdict) +{ + int ret = OK; + listitem_T *item; +#define CI_WHAT_MODE 0x01 +#define CI_WHAT_PUM_VISIBLE 0x02 +#define CI_WHAT_ITEMS 0x04 +#define CI_WHAT_SELECTED 0x08 +#define CI_WHAT_INSERTED 0x10 +#define CI_WHAT_ALL 0xff + int what_flag; + + if (what_list == NULL) + what_flag = CI_WHAT_ALL; + else + { + what_flag = 0; + for (item = what_list->lv_first; item != NULL; item = item->li_next) + { + char_u *what = tv_get_string(&item->li_tv); + + if (STRCMP(what, "mode") == 0) + what_flag |= CI_WHAT_MODE; + else if (STRCMP(what, "pum_visible") == 0) + what_flag |= CI_WHAT_PUM_VISIBLE; + else if (STRCMP(what, "items") == 0) + what_flag |= CI_WHAT_ITEMS; + else if (STRCMP(what, "selected") == 0) + what_flag |= CI_WHAT_SELECTED; + else if (STRCMP(what, "inserted") == 0) + what_flag |= CI_WHAT_INSERTED; + } + } + + if (ret == OK && (what_flag & CI_WHAT_MODE)) + ret = dict_add_string(retdict, "mode", ins_compl_mode()); + + if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) + ret = dict_add_number(retdict, "pum_visible", pum_visible()); + + if (ret == OK && (what_flag & CI_WHAT_ITEMS)) + { + list_T *li; + dict_T *di; + compl_T *match; + + li = list_alloc(); + if (li == NULL) + return; + ret = dict_add_list(retdict, "items", li); + if (ret == OK && compl_first_match != NULL) + { + match = compl_first_match; + do + { + if (!(match->cp_flags & ORIGINAL_TEXT)) + { + di = dict_alloc(); + if (di == NULL) + return; + ret = list_append_dict(li, di); + if (ret != OK) + return; + dict_add_string(di, "word", match->cp_str); + dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]); + dict_add_string(di, "menu", match->cp_text[CPT_MENU]); + dict_add_string(di, "kind", match->cp_text[CPT_KIND]); + dict_add_string(di, "info", match->cp_text[CPT_INFO]); + dict_add_string(di, "user_data", + match->cp_text[CPT_USER_DATA]); + } + match = match->cp_next; + } + while (match != NULL && match != compl_first_match); + } + } + + if (ret == OK && (what_flag & CI_WHAT_SELECTED)) + ret = dict_add_number(retdict, "selected", (compl_curr_match != NULL) ? + compl_curr_match->cp_number - 1 : -1); + + // TODO + // if (ret == OK && (what_flag & CI_WHAT_INSERTED)) +} + +/* + * Return Insert completion mode name string + */ + static char_u * +ins_compl_mode(void) +{ + if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || compl_started) + return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; + + return (char_u *)""; +} + /* * Delete one character before the cursor and show the subset of the matches * that match the word that is now before the cursor. diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -113,6 +113,7 @@ static void f_col(typval_T *argvars, typ static void f_complete(typval_T *argvars, typval_T *rettv); static void f_complete_add(typval_T *argvars, typval_T *rettv); static void f_complete_check(typval_T *argvars, typval_T *rettv); +static void f_complete_info(typval_T *argvars, typval_T *rettv); #endif static void f_confirm(typval_T *argvars, typval_T *rettv); static void f_copy(typval_T *argvars, typval_T *rettv); @@ -593,6 +594,7 @@ static struct fst {"complete", 2, 2, f_complete}, {"complete_add", 1, 1, f_complete_add}, {"complete_check", 0, 0, f_complete_check}, + {"complete_info", 0, 1, f_complete_info}, #endif {"confirm", 1, 4, f_confirm}, {"copy", 1, 1, f_copy}, @@ -2600,6 +2602,29 @@ f_complete_check(typval_T *argvars UNUSE rettv->vval.v_number = compl_interrupted; RedrawingDisabled = saved; } + +/* + * "complete_info()" function + */ + static void +f_complete_info(typval_T *argvars, typval_T *rettv) +{ + list_T *what_list = NULL; + + if (rettv_dict_alloc(rettv) != OK) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_listreq)); + return; + } + what_list = argvars[0].vval.v_list; + } + get_complete_info(what_list, rettv->vval.v_dict); +} #endif /* diff --git a/src/proto/edit.pro b/src/proto/edit.pro --- a/src/proto/edit.pro +++ b/src/proto/edit.pro @@ -18,6 +18,7 @@ void ins_compl_show_pum(void); char_u *find_word_start(char_u *ptr); char_u *find_word_end(char_u *ptr); int ins_compl_active(void); +void get_complete_info(list_T *what_list, dict_T *retdict); int ins_compl_add_tv(typval_T *tv, int dir); void ins_compl_check_keys(int frequency, int in_compl_func); int get_literal(void); 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 @@ -896,4 +896,105 @@ func Test_menu_only_exists_in_terminal() endtry endfunc +func Test_popup_complete_info_01() + new + inoremap =complete_info().mode + func s:complTestEval() abort + call complete(1, ['aa', 'ab']) + return '' + endfunc + inoremap =s:complTestEval() + call writefile([ + \ 'dummy dummy.txt 1', + \], 'Xdummy.txt') + setlocal tags=Xdummy.txt + setlocal dictionary=Xdummy.txt + setlocal thesaurus=Xdummy.txt + setlocal omnifunc=syntaxcomplete#Complete + setlocal completefunc=syntaxcomplete#Complete + setlocal spell + for [keys, mode_name] in [ + \ ["", ''], + \ ["\", 'ctrl_x'], + \ ["\\", 'keyword'], + \ ["\\", 'keyword'], + \ ["\\", 'whole_line'], + \ ["\\", 'files'], + \ ["\\", 'tags'], + \ ["\\", 'path_defines'], + \ ["\\", 'path_patterns'], + \ ["\\", 'dictionary'], + \ ["\\", 'thesaurus'], + \ ["\\", 'cmdline'], + \ ["\\", 'function'], + \ ["\\", 'omni'], + \ ["\s", 'spell'], + \ ["\", 'eval'], + \] + call feedkeys("i" . keys . "\\", 'tx') + call assert_equal(mode_name, getline('.')) + %d + endfor + call delete('Xdummy.txt') + bwipe! +endfunc + +func UserDefinedComplete(findstart, base) + if a:findstart + return 0 + else + return [ + \ { 'word': 'Jan', 'menu': 'January' }, + \ { 'word': 'Feb', 'menu': 'February' }, + \ { 'word': 'Mar', 'menu': 'March' }, + \ { 'word': 'Apr', 'menu': 'April' }, + \ { 'word': 'May', 'menu': 'May' }, + \ ] + endif +endfunc + +func GetCompleteInfo() + if empty(g:compl_what) + let g:compl_info = complete_info() + else + let g:compl_info = complete_info(g:compl_what) + endif + return '' +endfunc + +func Test_popup_complete_info_02() + new + inoremap =GetCompleteInfo() + setlocal completefunc=UserDefinedComplete + + let d = { + \ 'mode': 'function', + \ 'pum_visible': 1, + \ 'items': [ + \ {'word': 'Jan', 'menu': 'January', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, + \ {'word': 'Feb', 'menu': 'February', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, + \ {'word': 'Mar', 'menu': 'March', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, + \ {'word': 'Apr', 'menu': 'April', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, + \ {'word': 'May', 'menu': 'May', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''} + \ ], + \ 'selected': 0, + \ } + + let g:compl_what = [] + call feedkeys("i\\\", 'tx') + call assert_equal(d, g:compl_info) + + let g:compl_what = ['mode', 'pum_visible', 'selected'] + call remove(d, 'items') + call feedkeys("i\\\", 'tx') + call assert_equal(d, g:compl_info) + + let g:compl_what = ['mode'] + call remove(d, 'selected') + call remove(d, 'pum_visible') + call feedkeys("i\\\", 'tx') + call assert_equal(d, g:compl_info) + bwipe! +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 @@ -776,6 +776,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1068, +/**/ 1067, /**/ 1066,