Skip to content

Commit

Permalink
feat(completion): add shown key to complete_info()
Browse files Browse the repository at this point in the history
Problem: Currently there is no way of getting the list of completion items that are actually shown in the popupmenu. Which means completion providers have no way of knowing if a completion was exhausted.

Solution: Add a `shown` key to `complete_info()` which contains the indices of items that are shown in the popupmenu.

Ref: vim/vim#10007
  • Loading branch information
famiu committed Apr 30, 2024
1 parent efaf37a commit 40ec8dd
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 10 deletions.
2 changes: 2 additions & 0 deletions runtime/doc/builtin.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions runtime/doc/news.txt
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,9 @@ The following new APIs and features were added.
|vim.fs.root()| finds project root directories from a list of "root
markers".

|complete_info()| returns a `shown` item to indicate which items are shown on
the |ins-completion-menu|.

==============================================================================
CHANGED FEATURES *news-changed*

Expand Down
2 changes: 2 additions & 0 deletions runtime/lua/vim/_meta/vimfn.lua

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/nvim/eval.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,8 @@ M.funcs = {
dictionary containing the entries "word",
"abbr", "menu", "kind", "info" and "user_data".
See |complete-items|.
shown Indices of completion matches that are shown
in the |ins-completion-menu|. First index is zero.
selected Selected item index. First index is zero.
Index is -1 if no item is selected (showing
typed text only, or the last completion after
Expand Down
37 changes: 29 additions & 8 deletions src/nvim/insexpand.c
Original file line number Diff line number Diff line change
Expand Up @@ -2741,8 +2741,9 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
#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_SHOWN 0x08
#define CI_WHAT_SELECTED 0x10
#define CI_WHAT_INSERTED 0x20
#define CI_WHAT_ALL 0xff
int what_flag;

Expand All @@ -2761,6 +2762,8 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
what_flag |= CI_WHAT_PUM_VISIBLE;
} else if (strcmp(what, "items") == 0) {
what_flag |= CI_WHAT_ITEMS;
} else if (strcmp(what, "shown") == 0) {
what_flag |= CI_WHAT_SHOWN;
} else if (strcmp(what, "selected") == 0) {
what_flag |= CI_WHAT_SELECTED;
} else if (strcmp(what, "inserted") == 0) {
Expand All @@ -2778,26 +2781,38 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
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)) {
list_T *li = NULL;
if (ret == OK
&& (what_flag & CI_WHAT_ITEMS || what_flag & CI_WHAT_SHOWN || what_flag & CI_WHAT_SELECTED)) {
list_T *items = NULL;
list_T *shown = NULL;
int selected_idx = -1;
const int lead_len
= (what_flag & CI_WHAT_SHOWN) && compl_leader != NULL ? (int)strlen(compl_leader) : 0;

if (what_flag & CI_WHAT_ITEMS) {
li = tv_list_alloc(kListLenMayKnow);
ret = tv_dict_add_list(retdict, S_LEN("items"), li);
items = tv_list_alloc(kListLenMayKnow);
ret = tv_dict_add_list(retdict, S_LEN("items"), items);
}

if (ret == OK && (what_flag & CI_WHAT_SHOWN)) {
shown = tv_list_alloc(kListLenUnknown);
ret = tv_dict_add_list(retdict, S_LEN("shown"), shown);
}
if (ret == OK && what_flag & CI_WHAT_SELECTED) {

if (ret == OK && (what_flag & CI_WHAT_SELECTED)) {
if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) {
ins_compl_update_sequence_numbers();
}
}

if (ret == OK && compl_first_match != NULL) {
int list_idx = 0;
compl_T *match = compl_first_match;
do {
if (!match_at_original_text(match)) {
if (what_flag & CI_WHAT_ITEMS) {
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("abbr"), match->cp_text[CPT_ABBR]);
tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]);
Expand All @@ -2810,6 +2825,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);
}
}

if ((what_flag & CI_WHAT_SHOWN)
&& (compl_leader == NULL || ins_compl_equal(match, compl_leader, (size_t)lead_len))) {
tv_list_append_number(shown, list_idx);
}

if (compl_curr_match != NULL
&& compl_curr_match->cp_number == match->cp_number) {
selected_idx = list_idx;
Expand Down
28 changes: 28 additions & 0 deletions test/functional/editor/completion_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,34 @@ describe('completion', function()
]])
end)

describe('complete_info() shown', 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({ shown = { 0, 1, 2, 3 } }, fn.complete_info({ 'shown' }))

feed('a')
eq({ shown = { 0, 1 } }, fn.complete_info({ 'shown' }))

feed('c')
eq({ shown = { 1 } }, fn.complete_info({ 'shown' }))

feed('x')
eq({ shown = {} }, fn.complete_info({ 'shown' }))
end)
end)

-- oldtest: Test_complete_changed_complete_info()
it('no crash calling complete_info() in CompleteChanged', function()
source([[
Expand Down
5 changes: 3 additions & 2 deletions test/old/testdir/test_ins_complete.vim
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ func Test_completefunc_info()
set completeopt=menuone
set completefunc=CompleteTest
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{'shown': [0], 'pum_visible': 1, 'mode': 'function', 'selected': 0, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1))
bwipe!
set completeopt&
set completefunc&
Expand All @@ -406,7 +406,8 @@ func CompleteInfoTestUserDefinedFn(mvmt, idx, noselect)
set completefunc=CompleteInfoUserDefinedFn
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) : ''
call assert_equal(completed. "{'pum_visible': 1, 'mode': 'function', 'selected': " . a:idx . ", 'items': [" .
call assert_equal(completed. "{'shown': [0, 1, 2, 3], 'pum_visible': 1, 'mode': 'function', " .
\ "'selected': " . a:idx . ", 'items': [" .
\ "{'word': 'foo', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
\ "{'word': 'bar', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
\ "{'word': 'baz', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
Expand Down
3 changes: 3 additions & 0 deletions test/old/testdir/test_popup.vim
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,7 @@ func Test_popup_complete_info_02()
\ {'word': 'May', 'menu': 'May', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}
\ ],
\ 'selected': 0,
\ 'shown': [0, 1, 2, 3, 4],
\ }

let g:compl_what = []
Expand All @@ -1109,6 +1110,7 @@ func Test_popup_complete_info_02()

let g:compl_what = ['mode', 'pum_visible', 'selected']
call remove(d, 'items')
call remove(d, 'shown')
call feedkeys("i\<C-X>\<C-U>\<F5>", 'tx')
call assert_equal(d, g:compl_info)

Expand All @@ -1129,6 +1131,7 @@ func Test_popup_complete_info_no_pum()
\ 'pum_visible': 0,
\ 'items': [],
\ 'selected': -1,
\ 'shown': [],
\ }
call assert_equal( d, complete_info() )
bwipe!
Expand Down

0 comments on commit 40ec8dd

Please sign in to comment.