command_line_completion_spec.lua (9799B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 4 local clear = n.clear 5 local eq = t.eq 6 local exec_lua = n.exec_lua 7 8 --- @return { [1]: string[], [2]: integer } 9 local get_completions = function(input, env) 10 return exec_lua('return { vim._expand_pat(...) }', input, env) 11 end 12 13 --- @return { [1]: string[], [2]: integer } 14 local get_compl_parts = function(parts) 15 return exec_lua('return { vim._expand_pat_get_parts(...) }', parts) 16 end 17 18 before_each(clear) 19 20 describe('nlua_expand_pat', function() 21 it('completes exact matches', function() 22 eq({ { 'exact' }, 0 }, get_completions('exact', { exact = true })) 23 end) 24 25 it('returns empty table when nothing matches', function() 26 eq({ {}, 0 }, get_completions('foo', { bar = true })) 27 28 -- can access non-exist field 29 for _, m in ipairs { 30 'vim.', 31 'vim.lsp.', 32 'vim.treesitter.', 33 'vim.deepcopy.', 34 'vim.fn.', 35 'vim.api.', 36 'vim.o.', 37 'vim.b.', 38 } do 39 eq({ {}, m:len() }, get_completions(m .. 'foo')) 40 eq({ {}, 0 }, get_completions(m .. 'foo.')) 41 eq({ {}, 0 }, get_completions(m .. 'foo.bar')) 42 eq({ {}, 0 }, get_completions(m .. 'foo.bar.')) 43 end 44 end) 45 46 it('returns nice completions with function call prefix', function() 47 eq({ { 'FOO' }, 6 }, get_completions('print(F', { FOO = true, bawr = true })) 48 end) 49 50 it('returns keys for nested dicts', function() 51 eq( 52 { { 53 'nvim_buf_set_lines', 54 }, 8 }, 55 get_completions('vim.api.nvim_buf_', { 56 vim = { 57 api = { 58 nvim_buf_set_lines = true, 59 nvim_win_doesnt_match = true, 60 }, 61 other_key = true, 62 }, 63 }) 64 ) 65 end) 66 67 it('with colons', function() 68 eq( 69 { { 70 'bawr', 71 'baz', 72 }, 8 }, 73 get_completions('MyClass:b', { 74 MyClass = { 75 baz = true, 76 bawr = true, 77 foo = false, 78 }, 79 }) 80 ) 81 end) 82 83 it('returns keys after string key', function() 84 eq( 85 { { 86 'nvim_buf_set_lines', 87 }, 11 }, 88 get_completions('vim["api"].nvim_buf_', { 89 vim = { 90 api = { 91 nvim_buf_set_lines = true, 92 nvim_win_doesnt_match = true, 93 }, 94 other_key = true, 95 }, 96 }) 97 ) 98 99 eq( 100 { { 101 'nvim_buf_set_lines', 102 }, 21 }, 103 get_completions('vim["nested"]["api"].nvim_buf_', { 104 vim = { 105 nested = { 106 api = { 107 nvim_buf_set_lines = true, 108 nvim_win_doesnt_match = true, 109 }, 110 }, 111 other_key = true, 112 }, 113 }) 114 ) 115 end) 116 117 it('with lazy submodules of "vim" global', function() 118 eq({ { 'inspect', 'inspect_pos' }, 4 }, get_completions('vim.inspec')) 119 eq({ { 'treesitter' }, 4 }, get_completions('vim.treesi')) 120 eq({ { 'dev' }, 15 }, get_completions('vim.treesitter.de')) 121 eq({ { 'edit_query' }, 19 }, get_completions('vim.treesitter.dev.edit_')) 122 eq({ { 'set' }, 11 }, get_completions('vim.keymap.se')) 123 end) 124 125 it('include keys in mt.__index and ._submodules', function() 126 eq( 127 { { 'bar1', 'bar2', 'bar3' }, 4 }, 128 exec_lua(function() -- metatable cannot be serialized 129 return { 130 vim._expand_pat('foo.', { 131 foo = setmetatable( 132 { bar1 = true, _submodules = { bar2 = true } }, 133 { __index = { bar3 = true } } 134 ), 135 }), 136 } 137 end) 138 ) 139 end) 140 141 it('excludes private fields after "."', function() 142 eq( 143 { { 'bar' }, 4 }, 144 get_completions('foo.', { 145 foo = { 146 _bar = true, 147 bar = true, 148 }, 149 }) 150 ) 151 end) 152 153 it('includes private fields after "._"', function() 154 eq( 155 { { '_bar' }, 4 }, 156 get_completions('foo._', { 157 foo = { 158 _bar = true, 159 bar = true, 160 }, 161 }) 162 ) 163 end) 164 165 it('can interpolate globals', function() 166 eq( 167 { { 168 'nvim_buf_set_lines', 169 }, 12 }, 170 get_completions('vim[MY_VAR].nvim_buf_', { 171 MY_VAR = 'api', 172 vim = { 173 api = { 174 nvim_buf_set_lines = true, 175 nvim_win_doesnt_match = true, 176 }, 177 other_key = true, 178 }, 179 }) 180 ) 181 end) 182 183 describe('vim.fn', function() 184 it('simple completion', function() 185 local actual = get_completions('vim.fn.did') 186 local expected = { 187 { 'did_filetype' }, 188 #'vim.fn.', 189 } 190 eq(expected, actual) 191 end) 192 it('does not suggest "#" items', function() 193 exec_lua [[ 194 -- ensure remote#host#... functions exist 195 vim.cmd [=[ 196 runtime! autoload/remote/host.vim 197 ]=] 198 -- make a dummy call to ensure vim.fn contains an entry: remote#host#... 199 vim.fn['remote#host#IsRunning']('python3') 200 ]] 201 local actual = get_completions('vim.fn.remo') 202 local expected = { 203 { 'remove' }, -- there should be no completion "remote#host#..." 204 #'vim.fn.', 205 } 206 eq(expected, actual) 207 end) 208 end) 209 210 describe('completes', function() 211 it('vim.v', function() 212 local actual = get_completions('vim.v.t_') 213 local expected = { 214 { 't_blob', 't_bool', 't_dict', 't_float', 't_func', 't_list', 't_number', 't_string' }, 215 #'vim.v.', 216 } 217 eq(expected, actual) 218 end) 219 220 it('vim.g', function() 221 exec_lua [[ 222 vim.cmd [=[ 223 let g:nlua_foo = 'completion' 224 let g:nlua_foo_bar = 'completion' 225 let g:nlua_foo#bar = 'nocompletion' " should be excluded from lua completion 226 ]=] 227 ]] 228 local actual = get_completions('vim.g.nlua') 229 local expected = { 230 { 'nlua_foo', 'nlua_foo_bar' }, 231 #'vim.g.', 232 } 233 eq(expected, actual) 234 end) 235 236 it('vim.b', function() 237 exec_lua [[ 238 vim.b.nlua_foo_buf = 'bar' 239 vim.b.some_other_vars = 'bar' 240 ]] 241 local actual = get_completions('vim.b.nlua') 242 local expected = { 243 { 'nlua_foo_buf' }, 244 #'vim.b.', 245 } 246 eq(expected, actual) 247 end) 248 249 it('vim.w', function() 250 exec_lua [[ 251 vim.w.nlua_win_var = 42 252 ]] 253 local actual = get_completions('vim.w.nlua') 254 local expected = { 255 { 'nlua_win_var' }, 256 #'vim.w.', 257 } 258 eq(expected, actual) 259 end) 260 261 it('vim.t', function() 262 exec_lua [[ 263 vim.t.nlua_tab_var = 42 264 ]] 265 local actual = get_completions('vim.t.') 266 local expected = { 267 { 'nlua_tab_var' }, 268 #'vim.t.', 269 } 270 eq(expected, actual) 271 end) 272 273 it('vim.env', function() 274 exec_lua [[ 275 vim.env.NLUA_ENV_VAR = 'foo' 276 ]] 277 local actual = get_completions('vim.env.NLUA') 278 local expected = { 279 { 'NLUA_ENV_VAR' }, 280 #'vim.env.', 281 } 282 eq(expected, actual) 283 end) 284 end) 285 286 describe('completes', function() 287 -- for { vim.o, vim.go, vim.opt, vim.opt_local, vim.opt_global } 288 local test_opt = function(accessor) 289 it(accessor, function() 290 do 291 local actual = get_completions(accessor .. '.file') 292 local expected = { 293 'fileencoding', 294 'fileencodings', 295 'fileformat', 296 'fileformats', 297 'fileignorecase', 298 'filetype', 299 } 300 eq({ expected, #accessor + 1 }, actual, accessor .. '.file') 301 end 302 do 303 local actual = get_completions(accessor .. '.winh') 304 local expected = { 305 'winheight', 306 'winhighlight', 307 } 308 eq({ expected, #accessor + 1 }, actual, accessor .. '.winh') 309 end 310 end) 311 end 312 313 test_opt('vim.o') 314 test_opt('vim.go') 315 test_opt('vim.opt') 316 test_opt('vim.opt_local') 317 test_opt('vim.opt_global') 318 319 it('vim.o, suggesting all known options', function() 320 local completions = get_completions('vim.o.')[1] ---@type string[] 321 eq( 322 exec_lua [[ 323 return vim.tbl_count(vim.api.nvim_get_all_options_info()) 324 ]], 325 #completions 326 ) 327 end) 328 329 it('vim.bo', function() 330 do 331 local actual = get_completions('vim.bo.file') 332 local compls = { 333 -- should contain buffer options only 334 'fileencoding', 335 'fileformat', 336 'filetype', 337 } 338 eq({ compls, #'vim.bo.' }, actual) 339 end 340 do 341 local actual = get_completions('vim.bo.winh') 342 local compls = {} 343 eq({ compls, #'vim.bo.' }, actual) 344 end 345 end) 346 347 it('vim.wo', function() 348 do 349 local actual = get_completions('vim.wo.file') 350 local compls = {} 351 eq({ compls, #'vim.wo.' }, actual) 352 end 353 do 354 local actual = get_completions('vim.wo.winh') 355 -- should contain window options only 356 local compls = { 'winhighlight' } 357 eq({ compls, #'vim.wo.' }, actual) 358 end 359 end) 360 end) 361 362 it('returns everything if input is empty', function() 363 eq({ { 'other', 'vim' }, 0 }, get_completions('', { vim = true, other = true })) 364 end) 365 366 it('get_parts', function() 367 eq({ {}, 1 }, get_compl_parts('vim')) 368 eq({ { 'vim' }, 5 }, get_compl_parts('vim.ap')) 369 eq({ { 'vim', 'api' }, 9 }, get_compl_parts('vim.api.nvim_buf')) 370 eq({ { 'vim', 'api' }, 9 }, get_compl_parts('vim:api.nvim_buf')) 371 eq({ { 'vim', 'api' }, 12 }, get_compl_parts("vim['api'].nvim_buf")) 372 eq({ { 'vim', 'api' }, 12 }, get_compl_parts('vim["api"].nvim_buf')) 373 eq({ { 'vim', 'nested', 'api' }, 22 }, get_compl_parts('vim["nested"]["api"].nvim_buf')) 374 eq({ { 'vim', 'nested', 'api' }, 25 }, get_compl_parts('vim[ "nested" ]["api"].nvim_buf')) 375 eq({ { 'vim', { 'NESTED' }, 'api' }, 20 }, get_compl_parts('vim[NESTED]["api"].nvim_buf')) 376 end) 377 end)