diff options
Diffstat (limited to 'test/functional/editor/completion_spec.lua')
-rw-r--r-- | test/functional/editor/completion_spec.lua | 1196 |
1 files changed, 1196 insertions, 0 deletions
diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua new file mode 100644 index 0000000000..befad29922 --- /dev/null +++ b/test/functional/editor/completion_spec.lua @@ -0,0 +1,1196 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive +local clear, feed = helpers.clear, helpers.feed +local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq +local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect +local funcs = helpers.funcs +local curbufmeths = helpers.curbufmeths +local command = helpers.command +local meths = helpers.meths +local poke_eventloop = helpers.poke_eventloop + +describe('completion', function() + local screen + + before_each(function() + clear() + screen = Screen.new(60, 8) + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {background = Screen.colors.LightMagenta}, + [2] = {background = Screen.colors.Grey}, + [3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + [5] = {foreground = Screen.colors.Red}, + [6] = {background = Screen.colors.Black}, + [7] = {foreground = Screen.colors.White, background = Screen.colors.Red}, + [8] = {reverse = true}, + [9] = {bold = true, reverse = true}, + [10] = {foreground = Screen.colors.Grey0, background = Screen.colors.Yellow}, + }) + end) + + describe('v:completed_item', function() + it('is empty dict until completion', function() + eq({}, eval('v:completed_item')) + end) + it('is empty dict if the candidate is not inserted', function() + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + foo^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('<C-e>') + screen:expect([[ + foo | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('<ESC>') + eq({}, eval('v:completed_item')) + end) + it('returns expected dict in normal completion', function() + feed('ifoo<ESC>o<C-x><C-n>') + eq('foo', eval('getline(2)')) + eq({word = 'foo', abbr = '', menu = '', + info = '', kind = '', user_data = ''}, + eval('v:completed_item')) + end) + it('is readonly', function() + screen:try_resize(80, 8) + feed('ifoo<ESC>o<C-x><C-n><ESC>') + feed_command('let v:completed_item.word = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.abbr = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.menu = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.info = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.kind = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.user_data = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + end) + it('returns expected dict in omni completion', function() + source([[ + function! TestOmni(findstart, base) abort + return a:findstart ? 0 : [{'word': 'foo', 'abbr': 'bar', + \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'}, + \ {'word': 'word', 'abbr': 'abbr', 'menu': 'menu', + \ 'info': 'info', 'kind': 'kind'}] + endfunction + setlocal omnifunc=TestOmni + ]]) + feed('i<C-x><C-o>') + eq('foo', eval('getline(1)')) + screen:expect([[ + foo^ | + {2:bar foobaz baz }{0: }| + {1:abbr kind menu }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Omni completion (^O^N^P) }{4:match 1 of 2} | + ]]) + eq({word = 'foo', abbr = 'bar', menu = 'baz', + info = 'foobar', kind = 'foobaz', user_data = ''}, + eval('v:completed_item')) + end) + end) + + describe('completeopt', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, ['foo']) + return '' + endfunction + ]]) + end) + + it('inserts the first candidate if default', function() + feed_command('set completeopt+=menuone') + feed('ifoo<ESC>o') + screen:expect([[ + foo | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('<C-x>') + -- the ^X prompt, only test this once + screen:expect([[ + foo | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)} | + ]]) + feed('<C-n>') + screen:expect([[ + foo | + foo^ | + {2:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('bar<ESC>') + eq('foobar', eval('getline(2)')) + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + foobar | + foo^ | + {2:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + eq('foo', eval('getline(3)')) + end) + it('selects the first candidate if noinsert', function() + feed_command('set completeopt+=menuone,noinsert') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {2:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('<C-y>') + screen:expect([[ + foo | + foo^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('<ESC>') + eq('foo', eval('getline(2)')) + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + foo | + ^ | + {2:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('<C-y><ESC>') + eq('foo', eval('getline(3)')) + end) + it('does not insert the first candidate if noselect', function() + feed_command('set completeopt+=menuone,noselect') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {1:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('b') + screen:expect([[ + foo | + b^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('ar<ESC>') + eq('bar', eval('getline(2)')) + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + bar | + ^ | + {1:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('bar<ESC>') + eq('bar', eval('getline(3)')) + end) + it('does not select/insert the first candidate if noselect and noinsert', function() + feed_command('set completeopt+=menuone,noselect,noinsert') + feed('ifoo<ESC>o<C-x><C-n>') + screen:expect([[ + foo | + ^ | + {1:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('<ESC>') + screen:expect([[ + foo | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + eq('', eval('getline(2)')) + feed('o<C-r>=TestComplete()<CR>') + screen:expect([[ + foo | + | + ^ | + {1:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('<ESC>') + screen:expect([[ + foo | + | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + eq('', eval('getline(3)')) + end) + it('does not change modified state if noinsert', function() + feed_command('set completeopt+=menuone,noinsert') + feed_command('setlocal nomodified') + feed('i<C-r>=TestComplete()<CR><ESC>') + eq(0, eval('&l:modified')) + end) + it('does not change modified state if noselect', function() + feed_command('set completeopt+=menuone,noselect') + feed_command('setlocal nomodified') + feed('i<C-r>=TestComplete()<CR><ESC>') + eq(0, eval('&l:modified')) + end) + end) + + describe('completeopt+=noinsert does not add blank undo items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, ['foo', 'bar']) + return '' + endfunction + ]]) + feed_command('set completeopt+=noselect,noinsert') + feed_command('inoremap <right> <c-r>=TestComplete()<cr>') + end) + + local tests = { + ['<up>, <down>, <cr>'] = {'<down><cr>', '<up><cr>'}, + ['<c-n>, <c-p>, <c-y>'] = {'<c-n><c-y>', '<c-p><c-y>'}, + } + + for name, seq in pairs(tests) do + it('using ' .. name, function() + feed('iaaa<esc>') + feed('A<right>' .. seq[1] .. '<esc>') + feed('A<right><esc>A<right><esc>') + feed('A<cr>bbb<esc>') + feed('A<right>' .. seq[2] .. '<esc>') + feed('A<right><esc>A<right><esc>') + feed('A<cr>ccc<esc>') + feed('A<right>' .. seq[1] .. '<esc>') + feed('A<right><esc>A<right><esc>') + + local expected = { + {'foo', 'bar', 'foo'}, + {'foo', 'bar', 'ccc'}, + {'foo', 'bar'}, + {'foo', 'bbb'}, + {'foo'}, + {'aaa'}, + {''}, + } + + for i = 1, #expected do + if i > 1 then + feed('u') + end + eq(expected[i], eval('getline(1, "$")')) + end + + for i = #expected, 1, -1 do + if i < #expected then + feed('<c-r>') + end + eq(expected[i], eval('getline(1, "$")')) + end + end) + end + end) + + describe("refresh:always", function() + before_each(function() + source([[ + function! TestCompletion(findstart, base) abort + if a:findstart + let line = getline('.') + let start = col('.') - 1 + while start > 0 && line[start - 1] =~ '\a' + let start -= 1 + endwhile + return start + else + let ret = [] + for m in split("January February March April May June July August September October November December") + if m =~ a:base " match by regex + call add(ret, m) + endif + endfor + return {'words':ret, 'refresh':'always'} + endif + endfunction + + set completeopt=menuone,noselect + set completefunc=TestCompletion + ]]) + end ) + + it('completes on each input char', function () + feed('i<C-x><C-u>') + screen:expect([[ + ^ | + {1:January }{6: }{0: }| + {1:February }{6: }{0: }| + {1:March }{6: }{0: }| + {1:April }{2: }{0: }| + {1:May }{2: }{0: }| + {1:June }{2: }{0: }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('u') + screen:expect([[ + u^ | + {1:January }{0: }| + {1:February }{0: }| + {1:June }{0: }| + {1:July }{0: }| + {1:August }{0: }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('g') + screen:expect([[ + ug^ | + {1:August }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<Down>') + screen:expect([[ + ug^ | + {2:August }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) The only match} | + ]]) + feed('<C-y>') + screen:expect([[ + August^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + expect('August') + end) + + it("repeats correctly after backspace #2674", function () + feed('o<C-x><C-u>Ja') + screen:expect([[ + | + Ja^ | + {1:January }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<BS>') + screen:expect([[ + | + J^ | + {1:January }{0: }| + {1:June }{0: }| + {1:July }{0: }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('<C-n>') + screen:expect([[ + | + January^ | + {2:January }{0: }| + {1:June }{0: }| + {1:July }{0: }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{4:match 1 of 3} | + ]]) + feed('<C-n>') + screen:expect([[ + | + June^ | + {1:January }{0: }| + {2:June }{0: }| + {1:July }{0: }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{4:match 2 of 3} | + ]]) + feed('<Esc>') + screen:expect([[ + | + Jun^e | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed('.') + screen:expect([[ + | + June | + Jun^e | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + expect([[ + + June + June]]) + end) + end) + + describe('with a lot of items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, map(range(0,100), "string(v:val)")) + return '' + endfunction + ]]) + feed_command("set completeopt=menuone,noselect") + end) + + it("works", function() + feed('i<C-r>=TestComplete()<CR>') + screen:expect([[ + ^ | + {1:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {1:3 }{2: }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('7') + screen:expect([[ + 7^ | + {1:7 }{6: }{0: }| + {1:70 }{6: }{0: }| + {1:71 }{6: }{0: }| + {1:72 }{2: }{0: }| + {1:73 }{2: }{0: }| + {1:74 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<c-n>') + screen:expect([[ + 7^ | + {2:7 }{6: }{0: }| + {1:70 }{6: }{0: }| + {1:71 }{6: }{0: }| + {1:72 }{2: }{0: }| + {1:73 }{2: }{0: }| + {1:74 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<c-n>') + screen:expect([[ + 70^ | + {1:7 }{6: }{0: }| + {2:70 }{6: }{0: }| + {1:71 }{6: }{0: }| + {1:72 }{2: }{0: }| + {1:73 }{2: }{0: }| + {1:74 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + end) + + it('can be navigated with <PageDown>, <PageUp>', function() + feed('i<C-r>=TestComplete()<CR>') + screen:expect([[ + ^ | + {1:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {1:3 }{2: }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<PageDown>') + screen:expect([[ + ^ | + {1:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {2:3 }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<PageDown>') + screen:expect([[ + ^ | + {1:5 }{6: }{0: }| + {1:6 }{2: }{0: }| + {2:7 }{0: }| + {1:8 }{2: }{0: }| + {1:9 }{2: }{0: }| + {1:10 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<Down>') + screen:expect([[ + ^ | + {1:5 }{6: }{0: }| + {1:6 }{2: }{0: }| + {1:7 }{2: }{0: }| + {2:8 }{0: }| + {1:9 }{2: }{0: }| + {1:10 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<PageUp>') + screen:expect([[ + ^ | + {1:2 }{6: }{0: }| + {1:3 }{2: }{0: }| + {2:4 }{0: }| + {1:5 }{2: }{0: }| + {1:6 }{2: }{0: }| + {1:7 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- stop on first item + screen:expect([[ + ^ | + {2:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {1:3 }{2: }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- when on first item, unselect + screen:expect([[ + ^ | + {1:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {1:3 }{2: }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<PageUp>') -- when unselected, select last item + screen:expect([[ + ^ | + {1:95 }{2: }{0: }| + {1:96 }{2: }{0: }| + {1:97 }{2: }{0: }| + {1:98 }{2: }{0: }| + {1:99 }{2: }{0: }| + {2:100 }{6: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<PageUp>') + screen:expect([[ + ^ | + {1:94 }{2: }{0: }| + {1:95 }{2: }{0: }| + {2:96 }{0: }| + {1:97 }{2: }{0: }| + {1:98 }{2: }{0: }| + {1:99 }{6: }{0: }| + {3:-- INSERT --} | + ]]) + feed('<cr>') + screen:expect([[ + 96^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end) + end) + + it("does not indent until an item is selected #8345", function () + -- Indents on "ind", unindents on "unind". + source([[ + function! TestIndent() + let line = getline(v:lnum) + if (line =~ '^\s*ind') + return indent(v:lnum-1) + shiftwidth() + elseif (line =~ '^\s*unind') + return indent(v:lnum-1) - shiftwidth() + else + return indent(v:lnum-1) + endif + endfunction + set indentexpr=TestIndent() + set indentkeys=o,O,!^F,=ind,=unind + set completeopt+=menuone + ]]) + + -- Give some words to complete. + feed("iinc uninc indent unindent<CR>") + + -- Does not indent when "ind" is typed. + feed("in<C-X><C-N>") + -- Completion list is generated incorrectly if we send everything at once + -- via nvim_input(). So poke_eventloop() before sending <BS>. #8480 + poke_eventloop() + feed("<BS>d") + + screen:expect([[ + inc uninc indent unindent | + ind^ | + {2:indent }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2} | + ]]) + + -- Indents when the item is selected + feed("<C-Y>") + screen:expect([[ + inc uninc indent unindent | + indent^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + -- Indents when completion is exited using ESC. + feed("<CR>in<C-N><BS>d<Esc>") + screen:expect([[ + inc uninc indent unindent | + indent | + in^d | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + -- Works for unindenting too. + feed("ounin<C-X><C-N>") + helpers.poke_eventloop() + feed("<BS>d") + screen:expect([[ + inc uninc indent unindent | + indent | + ind | + unind^ | + {0:~ }{2: unindent }{0: }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2} | + ]]) + -- Works when going back and forth. + feed("<BS>c") + screen:expect([[ + inc uninc indent unindent | + indent | + ind | + uninc^ | + {0:~ }{2: uninc }{0: }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2} | + ]]) + feed("<BS>d") + screen:expect([[ + inc uninc indent unindent | + indent | + ind | + unind^ | + {0:~ }{2: unindent }{0: }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2} | + ]]) + feed("<C-N><C-N><C-Y><Esc>") + screen:expect([[ + inc uninc indent unindent | + indent | + ind | + uninden^t | + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) + + it('disables folding during completion', function () + feed_command("set foldmethod=indent") + feed('i<Tab>foo<CR><Tab>bar<Esc>gg') + screen:expect([[ + ^foo | + bar | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed('A<C-x><C-l>') + screen:expect([[ + foo^ | + bar | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Whole line completion (^L^N^P) }{7:Pattern not found} | + ]]) + eq(-1, eval('foldclosed(1)')) + end) + + it('popupmenu is not interrupted by events', function () + feed_command("set complete=.") + + feed('ifoobar fooegg<cr>f<c-p>') + screen:expect([[ + foobar fooegg | + fooegg^ | + {1:foobar }{0: }| + {2:fooegg }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + + assert_alive() + -- popupmenu still visible + screen:expect{grid=[[ + foobar fooegg | + fooegg^ | + {1:foobar }{0: }| + {2:fooegg }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]], unchanged=true} + + feed('<c-p>') + -- Didn't restart completion: old matches still used + screen:expect([[ + foobar fooegg | + foobar^ | + {2:foobar }{0: }| + {1:fooegg }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 2 of 2} | + ]]) + end) + + describe('lua completion', function() + it('expands when there is only one match', function() + feed(':lua CURRENT_TESTING_VAR = 1<CR>') + feed(':lua CURRENT_TESTING_<TAB>') + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + :lua CURRENT_TESTING_VAR^ | + ]]} + end) + + it('expands when there is only one match', function() + feed(':lua CURRENT_TESTING_FOO = 1<CR>') + feed(':lua CURRENT_TESTING_BAR = 1<CR>') + feed(':lua CURRENT_TESTING_<TAB>') + screen:expect{ grid = [[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {10:CURRENT_TESTING_BAR}{9: CURRENT_TESTING_FOO }| + :lua CURRENT_TESTING_BAR^ | + ]], unchanged = true } + end) + + it('provides completion from `getcompletion()`', function() + eq({'vim'}, funcs.getcompletion('vi', 'lua')) + eq({'api'}, funcs.getcompletion('vim.ap', 'lua')) + eq({'tbl_filter'}, funcs.getcompletion('vim.tbl_fil', 'lua')) + eq({'vim'}, funcs.getcompletion('print(vi', 'lua')) + end) + end) + + describe('from the commandline window', function() + it('is cleared after CTRL-C', function () + feed('q:') + feed('ifoo faa fee f') + screen:expect([[ + | + {8:[No Name] }| + {0::}foo faa fee f^ | + {0:~ }| + {0:~ }| + {0:~ }| + {9:[Command Line] }| + {3:-- INSERT --} | + ]] ) + feed('<c-x><c-n>') + screen:expect([[ + | + {8:[No Name] }| + {0::}foo faa fee foo^ | + {0:~ }{2: foo }{0: }| + {0:~ }{1: faa }{0: }| + {0:~ }{1: fee }{0: }| + {9:[Command Line] }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 3} | + ]]) + feed('<c-c>') + screen:expect([[ + | + {8:[No Name] }| + {0::}foo faa fee foo | + {0:~ }| + {0:~ }| + {0:~ }| + {9:[Command Line] }| + :foo faa fee foo^ | + ]]) + end) + end) + + describe('with numeric items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, g:_complist) + return '' + endfunction + ]]) + meths.set_option('completeopt', 'menuone,noselect') + meths.set_var('_complist', {{ + word=0, + abbr=1, + menu=2, + kind=3, + info=4, + icase=5, + dup=6, + empty=7, + }}) + end) + + it('shows correct variant as word', function() + feed('i<C-r>=TestComplete()<CR>') + screen:expect([[ + ^ | + {1:1 3 2 }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end) + end) + + it("'ignorecase' 'infercase' CTRL-X CTRL-N #6451", function() + feed_command('set ignorecase infercase') + feed_command('edit BACKERS.md') + feed('oX<C-X><C-N>') + screen:expect([[ + # Bountysource Backers | + Xnull^ | + {2:Xnull }{6: } | + {1:Xoxomoon }{6: }ryone who backed our [Bountysource fundraise| + {1:Xu }{6: }ountysource.com/teams/neovim/fundraiser)! | + {1:Xpayn }{2: } | + {1:Xinity }{2: }d URL in BACKERS.md. | + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 7} | + ]]) + end) + + it('TextChangedP autocommand', function() + curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar'}) + source([[ + set complete=. completeopt=menuone + let g:foo = [] + autocmd! TextChanged * :call add(g:foo, "N") + autocmd! TextChangedI * :call add(g:foo, "I") + autocmd! TextChangedP * :call add(g:foo, "P") + call cursor(3, 1) + ]]) + + command('let g:foo = []') + feed('o') + poke_eventloop() + feed('<esc>') + eq({'I'}, eval('g:foo')) + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + eq({'I', 'I'}, eval('g:foo')) + feed('<esc>') + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + eq({'I', 'I', 'P'}, eval('g:foo')) + feed('<esc>') + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + eq({'I', 'I', 'P', 'P'}, eval('g:foo')) + feed('<esc>') + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + eq({'I', 'I', 'P', 'P', 'P'}, eval('g:foo')) + feed('<esc>') + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + feed('<C-N>') + poke_eventloop() + feed('<C-N>') + eq({'I', 'I', 'P', 'P', 'P', 'P'}, eval('g:foo')) + feed('<esc>') + + eq({'foo', 'bar', 'foobar', 'foo'}, eval('getline(1, "$")')) + + source([[ + au! TextChanged + au! TextChangedI + au! TextChangedP + set complete&vim completeopt&vim + ]]) + end) + + it('CompleteChanged autocommand', function() + curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar', ''}) + source([[ + 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 + autocmd! CompleteChanged * :call OnPumChange() + call cursor(4, 1) + ]]) + + feed('Sf<C-N>') + screen:expect([[ + foo | + bar | + foobar | + f^ | + {1:foo }{0: }| + {1:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{5:Back at original} | + ]]) + eq({completed_item = {}, width = 15, + height = 2, size = 2, + col = 0, row = 4, scrollbar = false}, + eval('g:event')) + feed('<C-N>') + screen:expect([[ + foo | + bar | + foobar | + foo^ | + {2:foo }{0: }| + {1:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + eq('foo', eval('g:word')) + feed('<C-N>') + screen:expect([[ + foo | + bar | + foobar | + foobar^ | + {1:foo }{0: }| + {2:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 2 of 2} | + ]]) + eq('foobar', eval('g:word')) + feed('<up>') + screen:expect([[ + foo | + bar | + foobar | + foobar^ | + {2:foo }{0: }| + {1:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + eq('foo', eval('g:word')) + feed('<down>') + screen:expect([[ + foo | + bar | + foobar | + foobar^ | + {1:foo }{0: }| + {2:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 2 of 2} | + ]]) + eq('foobar', eval('g:word')) + feed('<esc>') + end) +end) |