inline_completion_spec.lua (11705B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local t_lsp = require('test.functional.plugin.lsp.testutil') 4 local Screen = require('test.functional.ui.screen') 5 6 local dedent = t.dedent 7 local eq = t.eq 8 9 local api = n.api 10 local exec_lua = n.exec_lua 11 local insert = n.insert 12 local feed = n.feed 13 14 local clear_notrace = t_lsp.clear_notrace 15 local create_server_definition = t_lsp.create_server_definition 16 17 describe('vim.lsp.inline_completion', function() 18 local text = dedent([[ 19 function fibonacci() 20 ]]) 21 22 local grid_without_candidates = dedent([[ 23 function fibonacci() | 24 ^ | 25 {1:~ }|*11 26 | 27 ]]) 28 29 local grid_with_candidates = dedent([[ 30 function fibonacci({1:n) {} | 31 {1: if (n <= 0) return 0;} | 32 {1: if (n === 1) return 1;} | 33 | 34 {1: let a = 0, b = 1, c;} | 35 {1: for (let i = 2; i <= n; i++) {} | 36 {1: c = a + b;} | 37 {1: a = b;} | 38 {1: b = c;} | 39 {1: }} | 40 {1: return b;} | 41 {1:}} | 42 ^ | 43 {3:-- INSERT --} | 44 ]]) 45 46 local grid_applied_candidates = dedent([[ 47 function fibonacci(n) { | 48 if (n <= 0) return 0; | 49 if (n === 1) return 1; | 50 | 51 let a = 0, b = 1, c; | 52 for (let i = 2; i <= n; i++) { | 53 c = a + b; | 54 a = b; | 55 b = c; | 56 } | 57 return b; | 58 ^} | 59 |*2 60 ]]) 61 62 --- @type test.functional.ui.screen 63 local screen 64 65 --- @type integer 66 local client_id 67 68 before_each(function() 69 clear_notrace() 70 exec_lua(create_server_definition) 71 72 screen = Screen.new() 73 screen:set_default_attr_ids({ 74 [1] = { bold = true, foreground = Screen.colors.Blue1 }, 75 [2] = { bold = true, foreground = Screen.colors.SeaGreen4 }, 76 [3] = { bold = true }, 77 }) 78 79 client_id = exec_lua(function() 80 _G.server = _G._create_server({ 81 capabilities = { 82 inlineCompletionProvider = true, 83 }, 84 handlers = { 85 ['textDocument/inlineCompletion'] = function(_, _, callback) 86 if _G.empty then 87 callback(nil, { 88 items = { 89 { 90 insertText = 'foobar', 91 range = { 92 start = { 93 line = 0, 94 character = 19, 95 }, 96 ['end'] = { 97 line = 0, 98 character = 19, 99 }, 100 }, 101 }, 102 }, 103 }) 104 return 105 end 106 107 callback(nil, { 108 items = { 109 { 110 command = { 111 command = 'dummy', 112 title = 'Completion Accepted', 113 }, 114 insertText = 'function fibonacci(n) {\n if (n <= 0) return 0;\n if (n === 1) return 1;\n\n let a = 0, b = 1, c;\n for (let i = 2; i <= n; i++) {\n c = a + b;\n a = b;\n b = c;\n }\n return b;\n}', 115 range = { 116 ['end'] = { 117 character = 20, 118 line = 0, 119 }, 120 start = { 121 character = 0, 122 line = 0, 123 }, 124 }, 125 }, 126 { 127 command = { 128 command = 'dummy', 129 title = 'Completion Accepted', 130 }, 131 insertText = 'function fibonacci(n) {\n if (n <= 0) return 0;\n if (n === 1) return 1;\n\n let a = 0, b = 1, c;\n for (let i = 2; i <= n; i++) {\n c = a + b;\n a = b;\n b = c;\n }\n return c;\n}', 132 range = { 133 ['end'] = { 134 character = 20, 135 line = 0, 136 }, 137 start = { 138 character = 0, 139 line = 0, 140 }, 141 }, 142 }, 143 { 144 command = { 145 command = 'dummy', 146 title = 'Completion Accepted', 147 }, 148 insertText = 'function fibonacci(n) {\n if (n < 0) {\n throw new Error("Input must be a non-negative integer.");\n }\n if (n === 0) return 0;\n if (n === 1) return 1;\n\n let a = 0, b = 1, c;\n for (let i = 2; i <= n; i++) {\n c = a + b;\n a = b;\n b = c;\n }\n return b;\n}', 149 range = { 150 ['end'] = { 151 character = 20, 152 line = 0, 153 }, 154 start = { 155 character = 0, 156 line = 0, 157 }, 158 }, 159 }, 160 }, 161 }) 162 end, 163 }, 164 }) 165 166 return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }) 167 end) 168 169 exec_lua(function() 170 local client = assert(vim.lsp.get_client_by_id(client_id)) 171 _G.called = false 172 client.commands.dummy = function() 173 _G.called = true 174 end 175 end) 176 177 insert(text) 178 feed('$') 179 exec_lua(function() 180 vim.lsp.inline_completion.enable() 181 end) 182 end) 183 184 after_each(function() 185 api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) 186 end) 187 188 describe('enable()', function() 189 it('requests or abort when entered/left insert mode', function() 190 screen:expect({ grid = grid_without_candidates }) 191 feed('i') 192 screen:expect({ grid = grid_with_candidates }) 193 feed('<Esc>') 194 screen:expect({ grid = grid_without_candidates }) 195 end) 196 197 it('no request when leaving insert mode immediately after typing', function() 198 screen:expect({ grid = grid_without_candidates }) 199 feed('ifoobar<Esc>') 200 screen:expect([[ 201 function fibonacci() | 202 fooba^r | 203 {1:~ }|*11 204 | 205 ]]) 206 screen:expect_unchanged(false, 500) 207 end) 208 end) 209 210 describe('get()', function() 211 it('applies the current candidate', function() 212 feed('i') 213 screen:expect({ grid = grid_with_candidates }) 214 exec_lua(function() 215 vim.lsp.inline_completion.get() 216 end) 217 n.poke_eventloop() 218 feed('<Esc>') 219 screen:expect({ grid = grid_applied_candidates }) 220 end) 221 222 it('correctly displays with absent/empty range', function() 223 exec_lua(function() 224 _G.empty = true 225 end) 226 feed('I') 227 screen:expect([[ 228 function fibonacci({1:foobar}) | 229 ^ | 230 {1:~ }|*11 231 {3:-- INSERT --} | 232 ]]) 233 end) 234 235 it('accepts on_accept callback', function() 236 feed('i') 237 screen:expect({ grid = grid_with_candidates }) 238 local result = exec_lua(function() 239 ---@type vim.lsp.inline_completion.Item 240 local result 241 vim.lsp.inline_completion.get({ 242 on_accept = function(item) 243 result = item 244 end, 245 }) 246 vim.wait(1000, function() 247 return result ~= nil 248 end) -- Wait for async callback. 249 return result 250 end) 251 feed('<Esc>') 252 screen:expect({ grid = grid_without_candidates }) 253 eq({ 254 _index = 1, 255 client_id = 1, 256 command = { 257 command = 'dummy', 258 title = 'Completion Accepted', 259 }, 260 insert_text = dedent([[ 261 function fibonacci(n) { 262 if (n <= 0) return 0; 263 if (n === 1) return 1; 264 265 let a = 0, b = 1, c; 266 for (let i = 2; i <= n; i++) { 267 c = a + b; 268 a = b; 269 b = c; 270 } 271 return b; 272 }]]), 273 range = { 274 end_ = { 275 buf = 1, 276 col = 20, 277 row = 0, 278 }, 279 start = { 280 buf = 1, 281 col = 0, 282 row = 0, 283 }, 284 }, 285 }, result) 286 end) 287 end) 288 289 describe('select()', function() 290 it('selects the next candidate', function() 291 feed('i') 292 screen:expect({ grid = grid_with_candidates }) 293 294 exec_lua(function() 295 vim.lsp.inline_completion.select() 296 end) 297 298 screen:expect([[ 299 function fibonacci({1:n) {} | 300 {1: if (n <= 0) return 0;} | 301 {1: if (n === 1) return 1;} | 302 | 303 {1: let a = 0, b = 1, c;} | 304 {1: for (let i = 2; i <= n; i++) {} | 305 {1: c = a + b;} | 306 {1: a = b;} | 307 {1: b = c;} | 308 {1: }} | 309 {1: return c;} | 310 {1:}}{2: (2/3)} | 311 ^ | 312 {3:-- INSERT --} | 313 ]]) 314 exec_lua(function() 315 vim.lsp.inline_completion.get() 316 end) 317 n.poke_eventloop() 318 feed('<Esc>') 319 screen:expect([[ 320 function fibonacci(n) { | 321 if (n <= 0) return 0; | 322 if (n === 1) return 1; | 323 | 324 let a = 0, b = 1, c; | 325 for (let i = 2; i <= n; i++) { | 326 c = a + b; | 327 a = b; | 328 b = c; | 329 } | 330 return c; | 331 ^} | 332 |*2 333 ]]) 334 end) 335 end) 336 end)