Files
neovim/test/functional/editor/completion_spec.lua
Sébastien Hoffmann 259e6fa9cc fix(ruler)!: consistent width in last line and statusline
Problem: Traditionally, the ruler in the last line is one cell shorter
than in the statusline, leaving the last cell of the screen blank.
According to code comments, this is in order to prevent unwanted
scrolling on "some" (unspecified, but presumably ancient) terminals.
Berkeley vi is more specific in its `vs_modeline` function: dumb
terminals with hardware scroll, SunOS 4.1.1 and Ultrix 4.2 curses.
(n)curses still has a similar limitation in `(w)addstr`, but apparently
only for historical reasons.

Maintaining the different widths leads to awkward inconsistencies when
the ruler is configured with 'rulerformat', except for the special case
where it contains a top-level `%=`. Shifting the ruler in the last line
to the left would be a solution, but the empty cell at the end doesn't
seem to be relevant anymore.

Solution: extend the ruler in the last line all the way to the right
edge of the screen, just like in the statusline. The exact same amount
of place will be available to the rest of the UI as before.

BREAKING CHANGE:
- the default ruler width is now 18 cells
- the last cell of the screen is no longer empty

Closes #41076
2026-08-04 06:36:58 +02:00

1928 lines
83 KiB
Lua

local t = require('test.testutil')
local describe, it, before_each, finally = t.describe, t.it, t.before_each, t.finally
local pcall_err = t.pcall_err
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local assert_alive = n.assert_alive
local clear, feed = n.clear, n.feed
local eval, eq, ok = n.eval, t.eq, t.ok
local source, expect = n.source, n.expect
local fn = n.fn
local command = n.command
local api = n.api
local poke_eventloop = n.poke_eventloop
local exec_lua = n.exec_lua
describe('completion', function()
local screen
before_each(function()
clear()
source([[
set completeopt-=noselect
" Avoid tags completion (if running test locally).
set complete-=t
]])
screen = Screen.new(60, 8)
screen:add_extra_attr_ids {
[100] = { foreground = Screen.colors.Gray0, background = Screen.colors.Yellow },
[101] = { background = Screen.colors.Gray0 },
[102] = { foreground = Screen.colors.SeaGreen },
}
end)
it('ctrl-x_ctrl-f completes Windows drive letter', function()
t.skip(not t.is_os('win'), 'N/A for non-Windows')
feed('iblablaC:/W<C-x><C-f>')
screen:expect {
any = [[C:\Windows\]],
}
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^ |
{1:~ }|*5
{5:-- Keyword Local completion (^N^P) The only match} |
]])
feed('<C-e>')
screen:expect([[
foo |
^ |
{1:~ }|*5
{5:-- 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>')
t.matches('E46%: ', t.pcall_err(command, 'let v:completed_item.word = "bar"'))
t.matches('E46%: ', t.pcall_err(command, 'let v:completed_item.abbr = "bar"'))
t.matches('E46%: ', t.pcall_err(command, 'let v:completed_item.menu = "bar"'))
t.matches('E46%: ', t.pcall_err(command, 'let v:completed_item.info = "bar"'))
t.matches('E46%: ', t.pcall_err(command, 'let v:completed_item.kind = "bar"'))
t.matches('E46%: ', t.pcall_err(command, 'let v:completed_item.user_data = "bar"'))
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^ |
{12:bar foobaz baz }{1: }|
{4:abbr kind menu }{1: }|
{1:~ }|*4
{5:-- Omni completion (^O^N^P) }{6: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()
command('set completeopt+=menuone')
feed('ifoo<ESC>o')
screen:expect([[
foo |
^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
feed('<C-x>')
-- the ^X prompt, only test this once
screen:expect([[
foo |
^ |
{1:~ }|*5
{5:-- ^X mode (^]^D^E^F^I^K^L^N^O^P^Rs^U^V^Y)} |
]])
feed('<C-n>')
screen:expect([[
foo |
foo^ |
{12:foo }{1: }|
{1:~ }|*4
{5:-- 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^ |
{12:foo }{1: }|
{1:~ }|*3
{5:-- INSERT --} |
]])
eq('foo', eval('getline(3)'))
end)
it('selects the first candidate if noinsert', function()
command('set completeopt+=menuone,noinsert')
feed('ifoo<ESC>o<C-x><C-n>')
screen:expect([[
foo |
^ |
{12:foo }{1: }|
{1:~ }|*4
{5:-- Keyword Local completion (^N^P) The only match} |
]])
feed('<C-y>')
screen:expect([[
foo |
foo^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
feed('<ESC>')
eq('foo', eval('getline(2)'))
feed('o<C-r>=TestComplete()<CR>')
screen:expect([[
foo |*2
^ |
{12:foo }{1: }|
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<C-y><ESC>')
eq('foo', eval('getline(3)'))
end)
it('does not insert the first candidate if noselect', function()
command('set completeopt+=menuone,noselect')
feed('ifoo<ESC>o<C-x><C-n>')
screen:expect([[
foo |
^ |
{4:foo }{1: }|
{1:~ }|*4
{5:-- Keyword Local completion (^N^P) }{19:Back at original} |
]])
feed('b')
screen:expect([[
foo |
b^ |
{1:~ }|*5
{5:-- Keyword Local completion (^N^P) }{19:Back at original} |
]])
feed('ar<ESC>')
eq('bar', eval('getline(2)'))
feed('o<C-r>=TestComplete()<CR>')
screen:expect([[
foo |
bar |
^ |
{4:foo }{1: }|
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('bar<ESC>')
eq('bar', eval('getline(3)'))
end)
it('does not select/insert the first candidate if noselect and noinsert', function()
command('set completeopt+=menuone,noselect,noinsert')
feed('ifoo<ESC>o<C-x><C-n>')
screen:expect([[
foo |
^ |
{4:foo }{1: }|
{1:~ }|*4
{5:-- Keyword Local completion (^N^P) }{19:Back at original} |
]])
feed('<ESC>')
screen:expect([[
foo |
^ |
{1:~ }|*5
|
]])
eq('', eval('getline(2)'))
feed('o<C-r>=TestComplete()<CR>')
screen:expect([[
foo |
|
^ |
{4:foo }{1: }|
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<ESC>')
screen:expect([[
foo |
|
^ |
{1:~ }|*4
|
]])
eq('', eval('getline(3)'))
end)
it('does not change modified state if noinsert', function()
command('set completeopt+=menuone,noinsert')
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()
command('set completeopt+=menuone,noselect')
command('setlocal nomodified')
feed('i<C-r>=TestComplete()<CR><ESC>')
eq(0, eval('&l:modified'))
end)
describe('"preselect"', function()
it('respects "preselect" of complete items', function()
source([[
function! TestComplete() abort
call complete(1, g:comp_items)
return ''
endfunction
]])
local function complete(items)
api.nvim_set_var('comp_items', items)
feed('<Esc>S<C-r>=TestComplete()<CR>')
end
local items = {
{ word = 'aaa' },
{ word = 'bbb' },
{ word = 'ccc', preselect = true },
{ word = 'ddd' },
}
command('set completeopt=menuone,preselect')
complete(items)
screen:expect([[
ccc^ |
{4:aaa }{1: }|
{4:bbb }{1: }|
{12:ccc }{1: }|
{4:ddd }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
-- scrolls pum when preselected item is far down
command('set pumheight=5')
local many = {} ---@type table[]
for i = 0, 19 do
many[i + 1] = { word = 'item' .. i }
end
many[16].preselect = true ---@type boolean
complete(many)
screen:expect([[
item15^ |
{4:item13 }{12: }{1: }|
{4:item14 }{12: }{1: }|
{12:item15 }{1: }|
{4:item16 }{101: }{1: }|
{4:item17 }{12: }{1: }|
{1:~ }|
{5:-- INSERT --} |
]])
-- preselected item is selected but not inserted with "noselect"
command('set completeopt=menuone,noselect,preselect pumheight=0')
complete(items)
screen:expect([[
^ |
{4:aaa }{1: }|
{4:bbb }{1: }|
{12:ccc }{1: }|
{4:ddd }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
-- selects the first preselected item that still matches
complete({
{ word = 'aaa', preselect = true },
{ word = 'abb' },
{ word = 'abc', preselect = true },
})
feed('ab<C-Y>')
eq('abc', api.nvim_get_current_line())
-- without "preselect" in 'completeopt' the attribute is ignored
command('set completeopt-=preselect')
complete(items)
feed('<C-N><C-Y>')
eq('aaa', api.nvim_get_current_line())
end)
it('does not insert text if noinsert', function()
source([[
function! TestComplete() abort
call complete(1, [{'word': 'aaa', 'preselect': v:true}, {'word': 'bbb'}])
return ''
endfunction
]])
command('set completeopt=menuone,noinsert,preselect')
feed('i<C-r>=TestComplete()<CR>')
eq('', api.nvim_get_current_line())
end)
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
]])
command('set completeopt+=noselect,noinsert')
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('with refresh:always and noselect', 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([[
^ |
{4:January }{101: }{1: }|
{4:February }{101: }{1: }|
{4:March }{101: }{1: }|
{4:April }{12: }{1: }|
{4:May }{12: }{1: }|
{4:June }{12: }{1: }|
{5:-- User defined completion (^U^N^P) }{19:Back at original} |
]])
feed('u')
screen:expect([[
u^ |
{4:January }{1: }|
{4:February }{1: }|
{4:June }{1: }|
{4:July }{1: }|
{4:August }{1: }|
{1:~ }|
{5:-- User defined completion (^U^N^P) }{19:Back at original} |
]])
feed('g')
screen:expect([[
ug^ |
{4:August }{1: }|
{1:~ }|*5
{5:-- User defined completion (^U^N^P) }{19:Back at original} |
]])
feed('<Down>')
screen:expect([[
ug^ |
{12:August }{1: }|
{1:~ }|*5
{5:-- User defined completion (^U^N^P) The only match} |
]])
feed('<C-y>')
screen:expect([[
August^ |
{1:~ }|*6
{5:-- INSERT --} |
]])
expect('August')
end)
it('repeats correctly after backspace #2674', function()
feed('o<C-x><C-u>Ja')
screen:expect([[
|
Ja^ |
{4:January }{1: }|
{1:~ }|*4
{5:-- User defined completion (^U^N^P) }{19:Back at original} |
]])
feed('<BS>')
screen:expect([[
|
J^ |
{4:January }{1: }|
{4:June }{1: }|
{4:July }{1: }|
{1:~ }|*2
{5:-- User defined completion (^U^N^P) }{19:Back at original} |
]])
feed('<C-n>')
screen:expect([[
|
January^ |
{12:January }{1: }|
{4:June }{1: }|
{4:July }{1: }|
{1:~ }|*2
{5:-- User defined completion (^U^N^P) }{6:match 1 of 3} |
]])
feed('<C-n>')
screen:expect([[
|
June^ |
{4:January }{1: }|
{12:June }{1: }|
{4:July }{1: }|
{1:~ }|*2
{5:-- User defined completion (^U^N^P) }{6:match 2 of 3} |
]])
feed('<Esc>')
screen:expect([[
|
Jun^e |
{1:~ }|*5
|
]])
feed('.')
screen:expect([[
|
June |
Jun^e |
{1:~ }|*4
|
]])
expect([[
June
June]])
end)
it('Enter inserts newline at original text after adding leader', function()
feed('iJ<C-x><C-u>')
poke_eventloop()
feed('u')
poke_eventloop()
feed('<CR>')
expect([[
Ju
]])
feed('J<C-x><C-u>')
poke_eventloop()
feed('<CR>')
expect([[
Ju
J
]])
end)
end)
describe('with noselect but not 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}
endif
endfunction
set completeopt=menuone,noselect
set completefunc=TestCompletion
]])
end)
it('Enter inserts newline at original text after adding leader', function()
feed('iJ<C-x><C-u>')
poke_eventloop()
feed('u')
poke_eventloop()
feed('<CR>')
expect([[
Ju
]])
feed('<Esc>')
poke_eventloop()
-- The behavior should be the same when completion has been interrupted,
-- which can happen interactively if the completion function is slow.
feed('ggVGSJ<C-x><C-u>u<CR>')
expect([[
Ju
]])
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
]])
command('set completeopt=menuone,noselect')
end)
it('works', function()
feed('i<C-r>=TestComplete()<CR>')
screen:expect([[
^ |
{4:0 }{101: }{1: }|
{4:1 }{12: }{1: }|
{4:2 }{12: }{1: }|
{4:3 }{12: }{1: }|
{4:4 }{12: }{1: }|
{4:5 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('7')
screen:expect([[
7^ |
{4:7 }{101: }{1: }|
{4:70 }{101: }{1: }|
{4:71 }{101: }{1: }|
{4:72 }{12: }{1: }|
{4:73 }{12: }{1: }|
{4:74 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<c-n>')
screen:expect([[
7^ |
{12:7 }{101: }{1: }|
{4:70 }{101: }{1: }|
{4:71 }{101: }{1: }|
{4:72 }{12: }{1: }|
{4:73 }{12: }{1: }|
{4:74 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<c-n>')
screen:expect([[
70^ |
{4:7 }{101: }{1: }|
{12:70 }{101: }{1: }|
{4:71 }{101: }{1: }|
{4:72 }{12: }{1: }|
{4:73 }{12: }{1: }|
{4:74 }{12: }{1: }|
{5:-- INSERT --} |
]])
end)
it('can be navigated with <PageDown>, <PageUp>', function()
feed('i<C-r>=TestComplete()<CR>')
screen:expect([[
^ |
{4:0 }{101: }{1: }|
{4:1 }{12: }{1: }|
{4:2 }{12: }{1: }|
{4:3 }{12: }{1: }|
{4:4 }{12: }{1: }|
{4:5 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<PageDown>')
screen:expect([[
^ |
{4:0 }{101: }{1: }|
{4:1 }{12: }{1: }|
{4:2 }{12: }{1: }|
{12:3 }{1: }|
{4:4 }{12: }{1: }|
{4:5 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<PageDown>')
screen:expect([[
^ |
{4:5 }{101: }{1: }|
{4:6 }{12: }{1: }|
{12:7 }{1: }|
{4:8 }{12: }{1: }|
{4:9 }{12: }{1: }|
{4:10 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<Down>')
screen:expect([[
^ |
{4:5 }{101: }{1: }|
{4:6 }{12: }{1: }|
{4:7 }{12: }{1: }|
{12:8 }{1: }|
{4:9 }{12: }{1: }|
{4:10 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<PageUp>')
screen:expect([[
^ |
{4:2 }{101: }{1: }|
{4:3 }{12: }{1: }|
{12:4 }{1: }|
{4:5 }{12: }{1: }|
{4:6 }{12: }{1: }|
{4:7 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<PageUp>') -- stop on first item
screen:expect([[
^ |
{12:0 }{101: }{1: }|
{4:1 }{12: }{1: }|
{4:2 }{12: }{1: }|
{4:3 }{12: }{1: }|
{4:4 }{12: }{1: }|
{4:5 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<PageUp>') -- when on first item, unselect
screen:expect([[
^ |
{4:0 }{101: }{1: }|
{4:1 }{12: }{1: }|
{4:2 }{12: }{1: }|
{4:3 }{12: }{1: }|
{4:4 }{12: }{1: }|
{4:5 }{12: }{1: }|
{5:-- INSERT --} |
]])
feed('<PageUp>') -- when unselected, select last item
screen:expect([[
^ |
{4:95 }{12: }{1: }|
{4:96 }{12: }{1: }|
{4:97 }{12: }{1: }|
{4:98 }{12: }{1: }|
{4:99 }{12: }{1: }|
{12:100 }{101: }{1: }|
{5:-- INSERT --} |
]])
feed('<PageUp>')
screen:expect([[
^ |
{4:94 }{12: }{1: }|
{4:95 }{12: }{1: }|
{12:96 }{1: }|
{4:97 }{12: }{1: }|
{4:98 }{12: }{1: }|
{4:99 }{101: }{1: }|
{5:-- INSERT --} |
]])
feed('<cr>')
screen:expect([[
96^ |
{1:~ }|*6
{5:-- 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^ |
{12:indent }{1: }|
{1:~ }|*4
{5:-- Keyword Local completion (^N^P) }{6:match 1 of 2} |
]])
-- Indents when the item is selected
feed('<C-Y>')
screen:expect([[
inc uninc indent unindent |
indent^ |
{1:~ }|*5
{5:-- 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 |
{1:~ }|*4
|
]])
-- Works for unindenting too.
feed('ounin<C-X><C-N>')
poke_eventloop()
feed('<BS>d')
screen:expect([[
inc uninc indent unindent |
indent |
ind |
unind^ |
{1:~ }{12: unindent }{1: }|
{1:~ }|*2
{5:-- Keyword Local completion (^N^P) }{6:match 1 of 2} |
]])
-- Works when going back and forth.
feed('<BS>c')
screen:expect([[
inc uninc indent unindent |
indent |
ind |
uninc^ |
{1:~ }{12: uninc }{1: }|
{1:~ }|*2
{5:-- Keyword Local completion (^N^P) }{6:match 1 of 2} |
]])
feed('<BS>d')
screen:expect([[
inc uninc indent unindent |
indent |
ind |
unind^ |
{1:~ }{12: unindent }{1: }|
{1:~ }|*2
{5:-- Keyword Local completion (^N^P) }{6:match 1 of 2} |
]])
feed('<C-N><C-N><C-Y><Esc>')
screen:expect([[
inc uninc indent unindent |
indent |
ind |
uninden^t |
{1:~ }|*3
|
]])
end)
it('disables folding during completion', function()
command('set foldmethod=indent')
feed('i<Tab>foo<CR><Tab>bar<Esc>gg')
screen:expect([[
^foo |
bar |
{1:~ }|*5
|
]])
feed('A<C-x><C-l>')
screen:expect([[
foo^ |
bar |
{1:~ }|*5
{5:-- Whole line completion (^L^N^P) }{9:Pattern not found} |
]])
eq(-1, eval('foldclosed(1)'))
end)
it('popupmenu is not interrupted by events', function()
command('set complete=.')
feed('ifoobar fooegg<cr>f<c-p>')
screen:expect([[
foobar fooegg |
fooegg^ |
{4:foobar }{1: }|
{12:fooegg }{1: }|
{1:~ }|*3
{5:-- Keyword completion (^N^P) }{6:match 1 of 2} |
]])
assert_alive()
-- popupmenu still visible
screen:expect {
grid = [[
foobar fooegg |
fooegg^ |
{4:foobar }{1: }|
{12:fooegg }{1: }|
{1:~ }|*3
{5:-- Keyword completion (^N^P) }{6:match 1 of 2} |
]],
unchanged = true,
}
feed('<c-p>')
-- Didn't restart completion: old matches still used
screen:expect([[
foobar fooegg |
foobar^ |
{12:foobar }{1: }|
{4:fooegg }{1: }|
{1:~ }|*3
{5:-- Keyword completion (^N^P) }{6: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 = [[
|
{1:~ }|*6
: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 = [[
|
{1:~ }|*5
{100:CURRENT_TESTING_BAR}{3: CURRENT_TESTING_FOO }|
:lua CURRENT_TESTING_BAR^ |
]],
unchanged = true,
}
end)
it('prefix is not included in completion for cmdline mode', function()
feed(':lua math.a<Tab>')
screen:expect([[
|
{1:~ }|*5
{100:abs}{3: acos asin atan atan2 }|
:lua math.abs^ |
]])
feed('<Tab>')
screen:expect([[
|
{1:~ }|*5
{3:abs }{100:acos}{3: asin atan atan2 }|
:lua math.acos^ |
]])
end)
it('prefix is not included in completion for i_CTRL-X_CTRL-V #19623', function()
feed('ilua math.a<C-X><C-V>')
screen:expect([[
lua math.abs^ |
{1:~ }{12: abs }{1: }|
{1:~ }{4: acos }{1: }|
{1:~ }{4: asin }{1: }|
{1:~ }{4: atan }{1: }|
{1:~ }{4: atan2 }{1: }|
{1:~ }|
{5:-- Command-line completion (^V^N^P) }{6:match 1 of 5} |
]])
feed('<C-V>')
screen:expect([[
lua math.acos^ |
{1:~ }{4: abs }{1: }|
{1:~ }{12: acos }{1: }|
{1:~ }{4: asin }{1: }|
{1:~ }{4: atan }{1: }|
{1:~ }{4: atan2 }{1: }|
{1:~ }|
{5:-- Command-line completion (^V^N^P) }{6:match 2 of 5} |
]])
end)
it('works when cursor is in the middle of cmdline #29586', function()
feed(':lua math.a(); 1<Left><Left><Left><Left><Left><Tab>')
screen:expect([[
|
{1:~ }|*5
{100:abs}{3: acos asin atan atan2 }|
:lua math.abs^(); 1 |
]])
end)
it('provides completion from `getcompletion()`', function()
eq({ 'vim' }, fn.getcompletion('vi', 'lua'))
eq({ 'api' }, fn.getcompletion('vim.ap', 'lua'))
eq({ 'tbl_filter' }, fn.getcompletion('vim.tbl_fil', 'lua'))
eq({ 'vim' }, fn.getcompletion('print(vi', 'lua'))
eq({ 'abs', 'acos', 'asin', 'atan', 'atan2' }, fn.getcompletion('math.a', 'lua'))
eq({ 'abs', 'acos', 'asin', 'atan', 'atan2' }, fn.getcompletion('lua math.a', 'cmdline'))
-- fuzzy completion is not supported, so the result should be the same
command('set wildoptions+=fuzzy')
eq({ 'vim' }, fn.getcompletion('vi', 'lua'))
end)
it('completes _defer_require() modules', function()
-- vim.lsp.c<tab> -> vim.lsp.completion
ok(vim.tbl_contains(fn.getcompletion('lua vim.lsp.c', 'cmdline'), 'completion'))
-- vim.lsp.completion.g<tab> -> vim.lsp.completion.get
ok(vim.tbl_contains(fn.getcompletion('lua vim.lsp.completion.g', 'cmdline'), 'get'))
end)
end)
it('cmdline completion supports various string options', function()
eq('auto', fn.getcompletion('set foldcolumn=', 'cmdline')[2])
eq({ 'nosplit', 'split' }, fn.getcompletion('set inccommand=', 'cmdline'))
eq({ 'ver:3,hor:6', 'hor:', 'ver:' }, fn.getcompletion('set mousescroll=', 'cmdline'))
eq('BS', fn.getcompletion('set termpastefilter=', 'cmdline')[2])
eq('SpecialKey', fn.getcompletion('set winhighlight=', 'cmdline')[1])
eq('SpecialKey', fn.getcompletion('set winhighlight=NonText:', 'cmdline')[1])
end)
it('cmdline completion for -complete does not contain spaces', function()
for _, str in ipairs(fn.getcompletion('command -complete=', 'cmdline')) do
ok(not str:find(' '), 'string without spaces', str)
end
end)
it('cmdline completion for :restart', function()
eq('qall', fn.getcompletion('restart +qa', 'cmdline')[1])
eq('edit', fn.getcompletion('restart +qall ed', 'cmdline')[1])
eq('edit', fn.getcompletion('restart ed', 'cmdline')[1])
end)
describe('from the commandline window', function()
it('is cleared after CTRL-C', function()
feed('q:')
feed('ifoo faa fee f')
screen:expect([[
|
{2:[No Name] }|
{1::}foo faa fee f^ |
{1:~ }|*3
{3:[Command Line] }|
{5:-- INSERT --} |
]])
feed('<c-x><c-n>')
screen:expect([[
|
{2:[No Name] }|
{1::}foo faa fee foo^ |
{1:~ }{12: foo }{1: }|
{1:~ }{4: faa }{1: }|
{1:~ }{4: fee }{1: }|
{3:[Command Line] }|
{5:-- Keyword Local completion (^N^P) }{6:match 1 of 3} |
]])
feed('<c-c>')
-- Non-blocking cmdwin (#40312): <C-C> closes the cmdwin and drops back
-- into ":" cmdline mode pre-filled with the line under the cursor.
screen:expect([[
|
{1:~ }|*6
: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
]])
api.nvim_set_option_value('completeopt', 'menuone,noselect', {})
api.nvim_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([[
^ |
{4:1 3 2 }{1: }|
{1:~ }|*5
{5:-- INSERT --} |
]])
end)
end)
it("'ignorecase' 'infercase' CTRL-X CTRL-N #6451", function()
command('set ignorecase infercase')
command('edit runtime/doc/credits.txt')
feed('oX<C-X><C-N>')
screen:expect {
grid = [[
*credits.txt* Nvim |
Xvi^ |
{12:Xvi }{101: } |
{4:Xvim }{101: } |
{4:X11 }{12: } NVIM REFERENCE MANUAL |
{4:Xnull }{12: } |
{4:Xoxomoon }{12: } |
{5:-- Keyword Local completion (^N^P) }{6:match 1 of 10} |
]],
}
end)
it('CompleteChanged autocommand', function()
api.nvim_buf_set_lines(0, 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)
]])
-- v:event.size should be set with ext_popupmenu #20646
screen:set_option('ext_popupmenu', true)
feed('Sf<C-N>')
screen:expect({
grid = [[
foo |
bar |
foobar |
f^ |
{1:~ }|*3
{5:-- Keyword completion (^N^P) }{19:Back at original} |
]],
popupmenu = {
anchor = { 1, 3, 0 },
items = { { 'foo', '', '', '' }, { 'foobar', '', '', '' } },
pos = -1,
},
})
eq(
{ completed_item = {}, width = 0, height = 2, size = 2, col = 0, row = 4, scrollbar = false },
eval('g:event')
)
feed('oob')
screen:expect({
grid = [[
foo |
bar |
foobar |
foob^ |
{1:~ }|*3
{5:-- Keyword completion (^N^P) }{19:Back at original} |
]],
popupmenu = {
anchor = { 1, 3, 0 },
items = { { 'foobar', '', '', '' } },
pos = -1,
},
})
eq(
{ completed_item = {}, width = 0, height = 1, size = 1, col = 0, row = 4, scrollbar = false },
eval('g:event')
)
feed('<Esc>')
screen:set_option('ext_popupmenu', false)
feed('Sf<C-N>')
screen:expect([[
foo |
bar |
foobar |
f^ |
{4:foo }{1: }|
{4:foobar }{1: }|
{1:~ }|
{5:-- Keyword completion (^N^P) }{19: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^ |
{12:foo }{1: }|
{4:foobar }{1: }|
{1:~ }|
{5:-- Keyword completion (^N^P) }{6:match 1 of 2} |
]])
eq('foo', eval('g:word'))
feed('<C-N>')
screen:expect([[
foo |
bar |
foobar |
foobar^ |
{4:foo }{1: }|
{12:foobar }{1: }|
{1:~ }|
{5:-- Keyword completion (^N^P) }{6:match 2 of 2} |
]])
eq('foobar', eval('g:word'))
feed('<up>')
screen:expect([[
foo |
bar |
foobar |
foobar^ |
{12:foo }{1: }|
{4:foobar }{1: }|
{1:~ }|
{5:-- Keyword completion (^N^P) }{6:match 1 of 2} |
]])
eq('foo', eval('g:word'))
feed('<down>')
screen:expect([[
foo |
bar |
foobar |
foobar^ |
{4:foo }{1: }|
{12:foobar }{1: }|
{1:~ }|
{5:-- Keyword completion (^N^P) }{6:match 2 of 2} |
]])
eq('foobar', eval('g:word'))
feed('<esc>')
end)
it('is stopped by :stopinsert from timer #12976', function()
screen:try_resize(32, 14)
command([[call setline(1, ['hello', 'hullo', 'heeee', ''])]])
feed('Gah<c-x><c-n>')
screen:expect([[
hello |
hullo |
heeee |
hello^ |
{12:hello }{1: }|
{4:hullo }{1: }|
{4:heeee }{1: }|
{1:~ }|*6
{5:-- }{6:match 1 of 3} |
]])
command([[call timer_start(100, { -> execute('stopinsert') })]])
vim.uv.sleep(200)
feed('k') -- cursor should move up in Normal mode
screen:expect([[
hello |
hullo |
heee^e |
hello |
{1:~ }|*9
|
]])
end)
-- oldtest: Test_complete_changed_complete_info()
it('no crash calling complete_info() in CompleteChanged', function()
source([[
set completeopt=menuone
autocmd CompleteChanged * call complete_info(['items'])
call feedkeys("iii\<cr>\<c-p>")
]])
screen:expect([[
ii |
ii^ |
{12:ii }{1: }|
{1:~ }|*4
{5:-- Keyword completion (^N^P) The only match} |
]])
assert_alive()
end)
it('no crash if text changed by first call to complete function #17489', function()
source([[
func Complete(findstart, base) abort
if a:findstart
let col = col('.')
call complete_add('#')
return col - 1
else
return []
endif
endfunc
set completeopt=longest
set completefunc=Complete
]])
feed('ifoo#<C-X><C-U>')
assert_alive()
end)
it('no crash using i_CTRL-X_CTRL-V to complete non-existent colorscheme', function()
feed('icolorscheme NOSUCHCOLORSCHEME<C-X><C-V>')
expect('colorscheme NOSUCHCOLORSCHEME')
assert_alive()
end)
it('complete with f flag #25598', function()
screen:try_resize(20, 9)
command('set complete+=f | edit foo | edit bar |edit foa |edit .hidden')
feed('i<C-n>')
screen:expect {
grid = [[
foo^ |
{12:foo }{1: }|
{4:bar }{1: }|
{4:foa }{1: }|
{4:.hidden }{1: }|
{1:~ }|*3
{5:-- }{6:match 1 of 4} |
]],
}
feed('<Esc>ccf<C-n>')
screen:expect {
grid = [[
foo^ |
{12:foo }{1: }|
{4:foa }{1: }|
{1:~ }|*5
{5:-- }{6:match 1 of 2} |
]],
}
end)
it('restores extmarks if original text is restored #23653', function()
screen:try_resize(screen._width, 4)
command([[
call setline(1, ['aaaa'])
let ns_id = nvim_create_namespace('extmark')
let mark_id = nvim_buf_set_extmark(0, ns_id, 0, 0, { 'end_col':2, 'hl_group':'Error' })
let mark = nvim_buf_get_extmark_by_id(0, ns_id, mark_id, { 'details':1 })
inoremap <C-x> <C-r>=Complete()<CR>
function Complete() abort
call complete(1, [{ 'word': 'aaaaa' }])
return ''
endfunction
]])
feed('A<C-X><C-E><Esc>')
eq(eval('mark'), eval("nvim_buf_get_extmark_by_id(0, ns_id, mark_id, { 'details':1 })"))
feed('A<C-N>')
eq(eval('mark'), eval("nvim_buf_get_extmark_by_id(0, ns_id, mark_id, { 'details':1 })"))
feed('<Esc>0Yppia<Esc>ggI<C-N>')
screen:expect([[
aaaa{9:^aa}aa |
{12:aaaa } |
{4:aaaaa } |
{5:-- Keyword completion (^N^P) }{6:match 1 of 2} |
]])
feed('<C-N><C-N><Esc>')
eq(eval('mark'), eval("nvim_buf_get_extmark_by_id(0, ns_id, mark_id, { 'details':1 })"))
feed('A<C-N>')
eq(eval('mark'), eval("nvim_buf_get_extmark_by_id(0, ns_id, mark_id, { 'details':1 })"))
feed('<C-N>')
screen:expect([[
aaaaa^ |
{4:aaaa } |
{12:aaaaa } |
{5:-- Keyword completion (^N^P) }{6:match 2 of 2} |
]])
feed('<C-E>')
screen:expect([[
{9:aa}aa^ |
aaaa |
aaaaa |
{5:-- INSERT --} |
]])
-- Also when completion leader is changed #31384
feed('<Esc>hi<C-N><C-P>a')
screen:expect({
grid = [[
{9:aa}a^aa |
{4:aaaa } |
{4:aaaaa } |
{5:-- Keyword completion (^N^P) }{19:Back at original} |
]],
})
-- But still grows with end_right_gravity #31437
command(
"call nvim_buf_set_extmark(0, ns_id, 1, 0, { 'end_col':2, 'hl_group':'Error', 'end_right_gravity': 1 })"
)
feed('<Esc>ji<C-N>a')
screen:expect({
grid = [[
{9:aa}aaa |
{9:aaa}^aa |
aaaaa |
{5:-- INSERT --} |
]],
})
end)
describe('nvim__complete_set', function()
it("fails when 'completeopt' does not include popup", function()
exec_lua([[
function _G.omni_test(findstart, base)
if findstart == 1 then
return vim.fn.col('.') - 1
end
return { { word = 'one' } }
end
vim.api.nvim_create_autocmd('CompleteChanged', {
callback = function()
local ok, err = pcall(vim.api.nvim__complete_set, 0, { info = '1info' })
if not ok then
vim.g.err_msg = err
end
end,
})
vim.opt.completeopt = 'menu,menuone'
vim.opt.omnifunc = 'v:lua.omni_test'
]])
feed('S<C-X><C-O>')
eq('completeopt option does not include popup', api.nvim_get_var('err_msg'))
end)
end)
it([[does not include buffer from non-focusable window for 'complete' "w"]], function()
local buf = api.nvim_create_buf(false, true)
local cfg = { focusable = false, relative = 'win', bufpos = { 1, 0 }, width = 1, height = 1 }
local win = api.nvim_open_win(buf, false, cfg)
api.nvim_buf_set_lines(buf, 0, -1, false, { 'foo' })
feed('i<C-N>')
screen:expect([[
^ |
{4:f}{1: }|
{1:~ }|*5
{5:-- Keyword completion (^N^P) }{9:Pattern not found} |
]])
api.nvim_win_set_config(win, { focusable = true })
feed('<Esc>i<C-N>')
screen:expect([[
foo^ |
{4:f}{1: }|
{1:~ }|*5
{5:-- Keyword completion (^N^P) The only match} |
]])
end)
-- oldtest: Test_shortmess()
it('shortmess+=c turns off completion messages', function()
command('set ruler')
command([[call setline(1, ['hello', 'hullo', 'heee'])]])
feed('Goh<C-N>')
screen:expect([[
hello |
hullo |
heee |
hello^ |
{12:hello }{1: }|
{4:hullo }{1: }|
{4:heee }{1: }|
{5:-- Keyword completion (^N^P) }{6:match 1 of 3} |
]])
feed('<Esc>')
command('set shm+=c')
feed('Sh<C-N>')
screen:expect([[
hello |
hullo |
heee |
hello^ |
{12:hello }{1: }|
{4:hullo }{1: }|
{4:heee }{1: }|
{5:-- INSERT --} 4,6 All|
]])
end)
-- oldtest: Test_autocompletedelay()
it("'autocompletedelay' option", function()
screen:try_resize(60, 10)
source([[
call setline(1, ['foo', 'foobar', 'foobarbaz'])
setlocal autocomplete
]])
screen:expect([[
^foo |
foobar |
foobarbaz |
{1:~ }|*6
|
]])
screen.timeout = 400
feed('Gof')
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{4:foobarbaz }{1: }|
{4:foobar }{1: }|
{4:foo }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<Esc>')
command('set autocompletedelay=500')
feed('Sf')
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
feed('o')
screen:expect([[
foo |
foobar |
foobarbaz |
fo^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
vim.uv.sleep(500)
screen:expect([[
foo |
foobar |
foobarbaz |
fo^ |
{4:foobarbaz }{1: }|
{4:foobar }{1: }|
{4:foo }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<BS>')
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{4:foobarbaz }{1: }|
{4:foobar }{1: }|
{4:foo }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
vim.uv.sleep(500)
screen:expect_unchanged()
-- During delay wait, user can open menu using CTRL_N completion
feed('<Esc>')
command('set completeopt=menuone')
feed('Sf<C-N>')
screen:expect([[
foo |
foobar |
foobarbaz |
foo^ |
{12:foo }{1: }|
{4:foobar }{1: }|
{4:foobarbaz }{1: }|
{1:~ }|*2
{5:-- Keyword completion (^N^P) }{6:match 1 of 3} |
]])
-- After the menu is open, ^N/^P and Up/Down should not delay.
-- Wait a bit longer than 'autocompletedelay' so the popup is surely shown
-- before sending CTRL-N, otherwise the keys race with the deferred popup.
feed('<Esc>')
command('set completeopt=menu')
feed('Sf')
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
vim.uv.sleep(600)
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{4:foobarbaz }{1: }|
{4:foobar }{1: }|
{4:foo }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<C-N>')
screen:expect([[
foo |
foobar |
foobarbaz |
foobarbaz^ |
{12:foobarbaz }{1: }|
{4:foobar }{1: }|
{4:foo }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<Down>')
screen:expect([[
foo |
foobar |
foobarbaz |
foobarbaz^ |
{4:foobarbaz }{1: }|
{12:foobar }{1: }|
{4:foo }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
-- When menu is not open Up/Down moves cursor to different line
feed('<Esc>Sf')
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
feed('<Up>')
screen:expect([[
foo |
foobar |
f^oobarbaz |
f |
{1:~ }|*5
{5:-- INSERT --} |
]])
feed('<Down>')
screen:expect([[
foo |
foobar |
foobarbaz |
f^ |
{1:~ }|*5
{5:-- INSERT --} |
]])
feed('<esc>')
end)
local function run_test_autocompletedelay_ctrl_k(delay1, delay2)
source([[
new
call setline(1, 'foo bar baz')
set autocomplete autocompletedelay=200
]])
feed('ob')
vim.uv.sleep(delay1)
feed('<C-K>')
vim.uv.sleep(delay2)
feed('.,<Esc>')
poke_eventloop()
expect('foo bar baz\nb…')
source([[
set autocomplete& autocompletedelay&
bwipe!
]])
end
-- oldtest: Test_autocompletedelay_ctrl_k()
it("'autocompletedelay' doesn't interfere with i_CTRL-K", function()
-- Ctrl-K typed after 'autocompletedelay' expires
run_test_autocompletedelay_ctrl_k(250, 500)
-- Ctrl-K typed before 'autocompletedelay' expires
run_test_autocompletedelay_ctrl_k(150, 500)
end)
-- oldtest: Test_fuzzy_select_item_when_acl()
it([[first item isn't selected with "fuzzy" and 'acl']], function()
screen:try_resize(60, 10)
source([[
call setline(1, ["v", "vi", "vim"])
set autocomplete completeopt=menuone,noinsert,fuzzy autocompletedelay=300
]])
feed('Govi')
screen:expect([[
v |
vi |
vim |
vi^ |
{4:vi }{1: }|
{4:vim }{1: }|
{1:~ }|*3
{5:-- INSERT --} |
]])
feed('<Esc>Sv')
screen:expect([[
v |
vi |
vim |
v^ |
{4:v }{1: }|
{4:vi }{1: }|
{4:vim }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('i')
screen:expect([[
v |
vi |
vim |
vi^ |
{4:vi }{1: }|
{4:vim }{1: }|
{1:~ }|*3
{5:-- INSERT --} |
]])
end)
-- oldtest: Test_autocompletedelay_longest_preinsert()
it("'autocompletedelay' with 'completeopt' longest/preinsert", function()
source([[
call setline(1, ['autocomplete', 'autocomxxx'])
set autocomplete completeopt+=longest autocompletedelay=500
]])
screen:expect([[
^autocomplete |
autocomxxx |
{1:~ }|*5
|
]])
screen.timeout = 400
-- No spurious characters when autocompletedelay is in effect
feed('Goau')
screen:expect([[
autocomplete |
autocomxxx |
au^ |
{1:~ }|*4
{5:-- INSERT --} |
]])
feed('toc')
screen:expect([[
autocomplete |
autocomxxx |
autoc^ |
{1:~ }|*4
{5:-- INSERT --} |
]])
vim.uv.sleep(500)
screen:expect([[
autocomplete |
autocomxxx |
autoc{102:^om} |
{4:autocomxxx }{1: }|
{4:autocomplete }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
-- Deleting a char should still show longest text
feed('<Esc>Saut')
screen:expect([[
autocomplete |
autocomxxx |
aut^ |
{1:~ }|*4
{5:-- INSERT --} |
]])
feed('<BS>')
screen:expect([[
autocomplete |
autocomxxx |
au^ |
{1:~ }|*4
{5:-- INSERT --} |
]])
vim.uv.sleep(500)
screen:expect([[
autocomplete |
autocomxxx |
au{102:^tocom} |
{4:autocomxxx }{1: }|
{4:autocomplete }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
-- Preinsert
command('set completeopt& completeopt+=preinsert')
feed('<Esc>Sau')
screen:expect([[
autocomplete |
autocomxxx |
au^ |
{1:~ }|*4
{5:-- INSERT --} |
]])
vim.uv.sleep(500)
screen:expect([[
autocomplete |
autocomxxx |
au{102:^tocomplete} |
{12:autocomplete }{1: }|
{4:autocomxxx }{1: }|
{1:~ }|*2
{5:-- INSERT --} |
]])
end)
-- oldtest: Test_fuzzy_filenames_compl_autocompl()
it('fuzzy file name does not crash with autocomplete', function()
feed('iset ac cot=fuzzy,longest<Esc>')
command('source')
feed('o')
poke_eventloop()
feed('.')
poke_eventloop()
feed('n')
poke_eventloop()
feed('a')
poke_eventloop()
feed('<C-X><C-F>') -- this used to cause segfault
screen:expect([[
set ac cot=fuzzy,longest |
.na^ |
{1:~ }|*5
{5:-- File name completion (^F^N^P) }{9:Pattern not found} |
]])
end)
it('returns all entries in the directory #25791', function()
n.mkdir_p('Xtest/sub1')
n.mkdir_p('Xtest/sub2')
t.write_file('Xtest/A', '')
t.write_file('Xtest/sub1/B', '')
t.write_file('Xtest/sub2/C', '')
finally(function()
n.rmdir('Xtest')
end)
local path = ('%s/Xtest/**'):format(vim.fs.normalize(vim.uv.cwd()))
command(('set path=%s'):format(path))
eq({ 'A', 'B', 'C', 'sub1/', 'sub2/' }, fn.getcompletion('find ', 'cmdline'))
end)
it('complete-items commit_chars', function()
exec_lua(function()
_G.Omnifunc = function(findstart, _)
return findstart == 1 and 0 or { { word = 'foobar', commit_chars = '(' } }
end
vim.bo.omnifunc = 'v:lua.Omnifunc'
end)
n.command('set completeopt=menuone,noinsert')
feed('i<C-x><C-o>')
poke_eventloop()
feed('(')
eq('foobar(', n.api.nvim_get_current_line())
end)
it('complete_info() reports equal, preselect and commit_chars', function()
command('set completeopt=menuone,noinsert')
source([[
function! TestComplete() abort
call complete(1, [
\ {'word': 'foo', 'equal': 1},
\ {'word': 'bar', 'preselect': v:true},
\ {'word': 'baz', 'commit_chars': '('},
\ {'word': 'qux'},
\ ])
return ''
endfunction
]])
feed('i<C-r>=TestComplete()<CR>')
poke_eventloop()
local items = fn.complete_info({ 'items' }).items
eq(4, #items)
eq({ 1, nil }, { items[1].equal, items[1].preselect })
eq(1, items[2].preselect)
eq('(', items[3].commit_chars)
eq({ nil, nil, nil }, { items[4].equal, items[4].preselect, items[4].commit_chars })
end)
end)