From 2d63b6d2c17906fafcbbfe7111202cf4c5d392af Mon Sep 17 00:00:00 2001 From: Shougo Matsushita Date: Sat, 30 Mar 2019 21:06:23 +0900 Subject: vim-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 vim/vim#4106) https://github.com/vim/vim/commit/fd133323d4e1cc9c0e61c0ce357df4d36ea148e3 --- src/nvim/edit.c | 126 ++++++++++++++++++++++++++++++++++++++-- src/nvim/eval.c | 16 +++++ src/nvim/eval.lua | 1 + src/nvim/testdir/test_popup.vim | 101 ++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index a0dce5ff7d..5304edb223 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -58,9 +58,10 @@ #include "nvim/os/input.h" #include "nvim/os/time.h" -/* - * 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 #define CTRL_X_NOT_DEFINED_YET 1 @@ -83,17 +84,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. static char *ctrl_x_msgs[] = { - N_(" Keyword completion (^N^P)"), /* ctrl_x_mode == 0, ^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, + 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, + NULL, // CTRL_X_FINISHED N_(" Dictionary completion (^K^N^P)"), N_(" Thesaurus completion (^T^N^P)"), N_(" Command-line completion (^V^N^P)"), @@ -104,6 +106,26 @@ static char *ctrl_x_msgs[] = 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"); static char e_complwin[] = N_("E839: Completion function changed window"); static char e_compldel[] = N_("E840: Completion function deleted text"); @@ -2980,6 +3002,98 @@ bool 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 = (char_u *)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 = tv_dict_add_str(retdict, S_LEN("mode"), + (const char *)ins_compl_mode()); + } + + if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) { + ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible()); + } + + if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { + list_T *li = tv_list_alloc(ins_compl_len()); + + ret = tv_dict_add_list(retdict, S_LEN("items"), li); + if (ret == OK && compl_first_match != NULL) { + compl_T *match = compl_first_match; + do { + if (!(match->cp_flags & ORIGINAL_TEXT)) { + dict_T *di = tv_dict_alloc(); + + tv_list_append_dict(li, di); + tv_dict_add_str(di, S_LEN("word"), + (const char *)match->cp_str); + tv_dict_add_str(di, S_LEN("abbr"), + (const char *)match->cp_text[CPT_ABBR]); + tv_dict_add_str(di, S_LEN("menu"), + (const char *)match->cp_text[CPT_MENU]); + tv_dict_add_str(di, S_LEN("kind"), + (const char *)match->cp_text[CPT_KIND]); + tv_dict_add_str(di, S_LEN("info"), + (const char *)match->cp_text[CPT_INFO]); + tv_dict_add_str(di, S_LEN("user_data"), + (const char *)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 = tv_dict_add_nr(retdict, S_LEN("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/nvim/eval.c b/src/nvim/eval.c index 0961e5fd92..71e572cf7e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7564,6 +7564,22 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) RedrawingDisabled = saved; } +// "complete_info()" function +static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) { + tv_dict_alloc_ret(rettv); + + list_T *what_list = NULL; + + 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); +} + /* * "confirm(message, buttons[, default [, type]])" function */ diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 78cac4878d..311897b3cd 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -64,6 +64,7 @@ return { complete={args=2}, complete_add={args=1}, complete_check={}, + complete_info={args={0, 1}}, confirm={args={1, 4}}, copy={args=1}, cos={args=1, func="float_op_wrapper", data="&cos"}, diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 6c43cbc1dc..c8398c3afa 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -712,4 +712,105 @@ func Test_popup_and_window_resize() bwipe! 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 -- cgit