mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 10:45:16 -07:00
feat(completion): add pum_matches
key to complete_info()
Problem: Currently there is no way of getting the list of completion items that are actually put in the popupmenu. Which means completion providers have no way of knowing if a completion was exhausted. Solution: Add a `pum_matches` key to `complete_info()` which contains the indices of items that are put in the popupmenu. Ref: https://github.com/vim/vim/issues/10007
This commit is contained in:
parent
efaf37a2b9
commit
c4f0235958
2
runtime/doc/builtin.txt
generated
2
runtime/doc/builtin.txt
generated
@ -796,6 +796,8 @@ complete_info([{what}]) *complete_info()*
|
|||||||
dictionary containing the entries "word",
|
dictionary containing the entries "word",
|
||||||
"abbr", "menu", "kind", "info" and "user_data".
|
"abbr", "menu", "kind", "info" and "user_data".
|
||||||
See |complete-items|.
|
See |complete-items|.
|
||||||
|
pum_matches Indices of completion matches that are put
|
||||||
|
in the |ins-completion-menu|. First index is zero.
|
||||||
selected Selected item index. First index is zero.
|
selected Selected item index. First index is zero.
|
||||||
Index is -1 if no item is selected (showing
|
Index is -1 if no item is selected (showing
|
||||||
typed text only, or the last completion after
|
typed text only, or the last completion after
|
||||||
|
@ -385,6 +385,9 @@ The following new APIs and features were added.
|
|||||||
• |vim.fs.root()| finds project root directories from a list of "root
|
• |vim.fs.root()| finds project root directories from a list of "root
|
||||||
markers".
|
markers".
|
||||||
|
|
||||||
|
• |complete_info()| returns a `pum_matches` item to indicate which items are put
|
||||||
|
in the |ins-completion-menu|.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
CHANGED FEATURES *news-changed*
|
CHANGED FEATURES *news-changed*
|
||||||
|
|
||||||
|
2
runtime/lua/vim/_meta/vimfn.lua
generated
2
runtime/lua/vim/_meta/vimfn.lua
generated
@ -1018,6 +1018,8 @@ function vim.fn.complete_check() end
|
|||||||
--- dictionary containing the entries "word",
|
--- dictionary containing the entries "word",
|
||||||
--- "abbr", "menu", "kind", "info" and "user_data".
|
--- "abbr", "menu", "kind", "info" and "user_data".
|
||||||
--- See |complete-items|.
|
--- See |complete-items|.
|
||||||
|
--- pum_matches Indices of completion matches that are put
|
||||||
|
--- in the |ins-completion-menu|. First index is zero.
|
||||||
--- selected Selected item index. First index is zero.
|
--- selected Selected item index. First index is zero.
|
||||||
--- Index is -1 if no item is selected (showing
|
--- Index is -1 if no item is selected (showing
|
||||||
--- typed text only, or the last completion after
|
--- typed text only, or the last completion after
|
||||||
|
@ -1367,6 +1367,8 @@ M.funcs = {
|
|||||||
dictionary containing the entries "word",
|
dictionary containing the entries "word",
|
||||||
"abbr", "menu", "kind", "info" and "user_data".
|
"abbr", "menu", "kind", "info" and "user_data".
|
||||||
See |complete-items|.
|
See |complete-items|.
|
||||||
|
pum_matches Indices of completion matches that are put
|
||||||
|
in the |ins-completion-menu|. First index is zero.
|
||||||
selected Selected item index. First index is zero.
|
selected Selected item index. First index is zero.
|
||||||
Index is -1 if no item is selected (showing
|
Index is -1 if no item is selected (showing
|
||||||
typed text only, or the last completion after
|
typed text only, or the last completion after
|
||||||
|
@ -2741,8 +2741,9 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
|
|||||||
#define CI_WHAT_MODE 0x01
|
#define CI_WHAT_MODE 0x01
|
||||||
#define CI_WHAT_PUM_VISIBLE 0x02
|
#define CI_WHAT_PUM_VISIBLE 0x02
|
||||||
#define CI_WHAT_ITEMS 0x04
|
#define CI_WHAT_ITEMS 0x04
|
||||||
#define CI_WHAT_SELECTED 0x08
|
#define CI_WHAT_PUM_MATCHES 0x08
|
||||||
#define CI_WHAT_INSERTED 0x10
|
#define CI_WHAT_SELECTED 0x10
|
||||||
|
#define CI_WHAT_INSERTED 0x20
|
||||||
#define CI_WHAT_ALL 0xff
|
#define CI_WHAT_ALL 0xff
|
||||||
int what_flag;
|
int what_flag;
|
||||||
|
|
||||||
@ -2761,6 +2762,8 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
|
|||||||
what_flag |= CI_WHAT_PUM_VISIBLE;
|
what_flag |= CI_WHAT_PUM_VISIBLE;
|
||||||
} else if (strcmp(what, "items") == 0) {
|
} else if (strcmp(what, "items") == 0) {
|
||||||
what_flag |= CI_WHAT_ITEMS;
|
what_flag |= CI_WHAT_ITEMS;
|
||||||
|
} else if (strcmp(what, "pum_matches") == 0) {
|
||||||
|
what_flag |= CI_WHAT_PUM_MATCHES;
|
||||||
} else if (strcmp(what, "selected") == 0) {
|
} else if (strcmp(what, "selected") == 0) {
|
||||||
what_flag |= CI_WHAT_SELECTED;
|
what_flag |= CI_WHAT_SELECTED;
|
||||||
} else if (strcmp(what, "inserted") == 0) {
|
} else if (strcmp(what, "inserted") == 0) {
|
||||||
@ -2778,18 +2781,31 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
|
|||||||
ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible());
|
ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == OK && (what_flag & CI_WHAT_ITEMS || what_flag & CI_WHAT_SELECTED)) {
|
if (ret == OK
|
||||||
list_T *li = NULL;
|
&& (what_flag & CI_WHAT_ITEMS || what_flag & CI_WHAT_PUM_MATCHES
|
||||||
|
|| what_flag & CI_WHAT_SELECTED)) {
|
||||||
|
list_T *items = NULL;
|
||||||
|
list_T *pum_matches = NULL;
|
||||||
int selected_idx = -1;
|
int selected_idx = -1;
|
||||||
|
const int lead_len
|
||||||
|
= (what_flag & CI_WHAT_PUM_MATCHES) && compl_leader != NULL ? (int)strlen(compl_leader) : 0;
|
||||||
|
|
||||||
if (what_flag & CI_WHAT_ITEMS) {
|
if (what_flag & CI_WHAT_ITEMS) {
|
||||||
li = tv_list_alloc(kListLenMayKnow);
|
items = tv_list_alloc(kListLenMayKnow);
|
||||||
ret = tv_dict_add_list(retdict, S_LEN("items"), li);
|
ret = tv_dict_add_list(retdict, S_LEN("items"), items);
|
||||||
}
|
}
|
||||||
if (ret == OK && what_flag & CI_WHAT_SELECTED) {
|
|
||||||
|
if (ret == OK && (what_flag & CI_WHAT_PUM_MATCHES)) {
|
||||||
|
pum_matches = tv_list_alloc(kListLenUnknown);
|
||||||
|
ret = tv_dict_add_list(retdict, S_LEN("pum_matches"), pum_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == OK && (what_flag & CI_WHAT_SELECTED)) {
|
||||||
if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) {
|
if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) {
|
||||||
ins_compl_update_sequence_numbers();
|
ins_compl_update_sequence_numbers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == OK && compl_first_match != NULL) {
|
if (ret == OK && compl_first_match != NULL) {
|
||||||
int list_idx = 0;
|
int list_idx = 0;
|
||||||
compl_T *match = compl_first_match;
|
compl_T *match = compl_first_match;
|
||||||
@ -2797,7 +2813,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
|
|||||||
if (!match_at_original_text(match)) {
|
if (!match_at_original_text(match)) {
|
||||||
if (what_flag & CI_WHAT_ITEMS) {
|
if (what_flag & CI_WHAT_ITEMS) {
|
||||||
dict_T *di = tv_dict_alloc();
|
dict_T *di = tv_dict_alloc();
|
||||||
tv_list_append_dict(li, di);
|
tv_list_append_dict(items, di);
|
||||||
tv_dict_add_str(di, S_LEN("word"), match->cp_str);
|
tv_dict_add_str(di, S_LEN("word"), match->cp_str);
|
||||||
tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
|
tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
|
||||||
tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]);
|
tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]);
|
||||||
@ -2810,6 +2826,12 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
|
|||||||
tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data);
|
tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((what_flag & CI_WHAT_PUM_MATCHES)
|
||||||
|
&& (compl_leader == NULL || ins_compl_equal(match, compl_leader, (size_t)lead_len))) {
|
||||||
|
tv_list_append_number(pum_matches, list_idx);
|
||||||
|
}
|
||||||
|
|
||||||
if (compl_curr_match != NULL
|
if (compl_curr_match != NULL
|
||||||
&& compl_curr_match->cp_number == match->cp_number) {
|
&& compl_curr_match->cp_number == match->cp_number) {
|
||||||
selected_idx = list_idx;
|
selected_idx = list_idx;
|
||||||
|
@ -1080,6 +1080,34 @@ describe('completion', function()
|
|||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('complete_info() pum_matches', function()
|
||||||
|
before_each(function()
|
||||||
|
source([[
|
||||||
|
function! TestComplete() abort
|
||||||
|
call complete(1, ['abcd', 'acbd', 'xyz', 'foobar'])
|
||||||
|
return ''
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
set completeopt+=noinsert
|
||||||
|
inoremap <C-x> <c-r>=TestComplete()<cr>
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works', function()
|
||||||
|
feed('i<C-x>')
|
||||||
|
eq({ pum_matches = { 0, 1, 2, 3 } }, fn.complete_info({ 'pum_matches' }))
|
||||||
|
|
||||||
|
feed('a')
|
||||||
|
eq({ pum_matches = { 0, 1 } }, fn.complete_info({ 'pum_matches' }))
|
||||||
|
|
||||||
|
feed('c')
|
||||||
|
eq({ pum_matches = { 1 } }, fn.complete_info({ 'pum_matches' }))
|
||||||
|
|
||||||
|
feed('x')
|
||||||
|
eq({ pum_matches = {} }, fn.complete_info({ 'pum_matches' }))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
-- oldtest: Test_complete_changed_complete_info()
|
-- oldtest: Test_complete_changed_complete_info()
|
||||||
it('no crash calling complete_info() in CompleteChanged', function()
|
it('no crash calling complete_info() in CompleteChanged', function()
|
||||||
source([[
|
source([[
|
||||||
|
@ -382,7 +382,7 @@ func Test_completefunc_info()
|
|||||||
set completeopt=menuone
|
set completeopt=menuone
|
||||||
set completefunc=CompleteTest
|
set completefunc=CompleteTest
|
||||||
call feedkeys("i\<C-X>\<C-U>\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
|
call feedkeys("i\<C-X>\<C-U>\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
|
||||||
call assert_equal("matched{'pum_visible': 1, 'mode': 'function', 'selected': 0, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1))
|
call assert_equal("matched{'pum_matches': [0], 'pum_visible': 1, 'mode': 'function', 'selected': 0, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1))
|
||||||
bwipe!
|
bwipe!
|
||||||
set completeopt&
|
set completeopt&
|
||||||
set completefunc&
|
set completefunc&
|
||||||
@ -406,7 +406,8 @@ func CompleteInfoTestUserDefinedFn(mvmt, idx, noselect)
|
|||||||
set completefunc=CompleteInfoUserDefinedFn
|
set completefunc=CompleteInfoUserDefinedFn
|
||||||
call feedkeys("i\<C-X>\<C-U>" . a:mvmt . "\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
|
call feedkeys("i\<C-X>\<C-U>" . a:mvmt . "\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
|
||||||
let completed = a:idx != -1 ? ['foo', 'bar', 'baz', 'qux']->get(a:idx) : ''
|
let completed = a:idx != -1 ? ['foo', 'bar', 'baz', 'qux']->get(a:idx) : ''
|
||||||
call assert_equal(completed. "{'pum_visible': 1, 'mode': 'function', 'selected': " . a:idx . ", 'items': [" .
|
call assert_equal(completed. "{'pum_matches': [0, 1, 2, 3], 'pum_visible': 1, " .
|
||||||
|
\ "'mode': 'function', 'selected': " . a:idx . ", 'items': [" .
|
||||||
\ "{'word': 'foo', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
|
\ "{'word': 'foo', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
|
||||||
\ "{'word': 'bar', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
|
\ "{'word': 'bar', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
|
||||||
\ "{'word': 'baz', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
|
\ "{'word': 'baz', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
|
||||||
|
@ -1101,6 +1101,7 @@ func Test_popup_complete_info_02()
|
|||||||
\ {'word': 'May', 'menu': 'May', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}
|
\ {'word': 'May', 'menu': 'May', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}
|
||||||
\ ],
|
\ ],
|
||||||
\ 'selected': 0,
|
\ 'selected': 0,
|
||||||
|
\ 'pum_matches': [0, 1, 2, 3, 4],
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
let g:compl_what = []
|
let g:compl_what = []
|
||||||
@ -1109,6 +1110,7 @@ func Test_popup_complete_info_02()
|
|||||||
|
|
||||||
let g:compl_what = ['mode', 'pum_visible', 'selected']
|
let g:compl_what = ['mode', 'pum_visible', 'selected']
|
||||||
call remove(d, 'items')
|
call remove(d, 'items')
|
||||||
|
call remove(d, 'pum_matches')
|
||||||
call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
|
call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
|
||||||
call assert_equal(d, g:compl_info)
|
call assert_equal(d, g:compl_info)
|
||||||
|
|
||||||
@ -1129,6 +1131,7 @@ func Test_popup_complete_info_no_pum()
|
|||||||
\ 'pum_visible': 0,
|
\ 'pum_visible': 0,
|
||||||
\ 'items': [],
|
\ 'items': [],
|
||||||
\ 'selected': -1,
|
\ 'selected': -1,
|
||||||
|
\ 'pum_matches': [],
|
||||||
\ }
|
\ }
|
||||||
call assert_equal( d, complete_info() )
|
call assert_equal( d, complete_info() )
|
||||||
bwipe!
|
bwipe!
|
||||||
|
Loading…
Reference in New Issue
Block a user