1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045 |
- ---@diagnostic disable: no-unknown
- local t = require('test.testutil')
- local t_lsp = require('test.functional.plugin.lsp.testutil')
- local n = require('test.functional.testnvim')()
- local clear = n.clear
- local eq = t.eq
- local neq = t.neq
- local exec_lua = n.exec_lua
- local feed = n.feed
- local retry = t.retry
- local create_server_definition = t_lsp.create_server_definition
- --- Convert completion results.
- ---
- ---@param line string line contents. Mark cursor position with `|`
- ---@param candidates lsp.CompletionList|lsp.CompletionItem[]
- ---@param lnum? integer 0-based, defaults to 0
- ---@return {items: table[], server_start_boundary: integer?}
- local function complete(line, candidates, lnum, server_boundary)
- lnum = lnum or 0
- -- nvim_win_get_cursor returns 0 based column, line:find returns 1 based
- local cursor_col = line:find('|') - 1
- line = line:gsub('|', '')
- return exec_lua(function(result)
- local line_to_cursor = line:sub(1, cursor_col)
- local client_start_boundary = vim.fn.match(line_to_cursor, '\\k*$')
- local items, new_server_boundary = require('vim.lsp.completion')._convert_results(
- line,
- lnum,
- cursor_col,
- 1,
- client_start_boundary,
- server_boundary,
- result,
- 'utf-16'
- )
- return {
- items = items,
- server_start_boundary = new_server_boundary,
- }
- end, candidates)
- end
- describe('vim.lsp.completion: item conversion', function()
- before_each(n.clear)
- -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
- it('prefers textEdit over label as word', function()
- local range0 = {
- start = { line = 0, character = 0 },
- ['end'] = { line = 0, character = 0 },
- }
- local completion_list = {
- -- resolves into label
- { label = 'foobar', sortText = 'a', documentation = 'documentation' },
- {
- label = 'foobar',
- sortText = 'b',
- documentation = { value = 'documentation' },
- },
- -- resolves into insertText
- { label = 'foocar', sortText = 'c', insertText = 'foobar' },
- { label = 'foocar', sortText = 'd', insertText = 'foobar' },
- -- resolves into textEdit.newText
- {
- label = 'foocar',
- sortText = 'e',
- insertText = 'foodar',
- textEdit = { newText = 'foobar', range = range0 },
- },
- { label = 'foocar', sortText = 'f', textEdit = { newText = 'foobar', range = range0 } },
- -- plain text
- {
- label = 'foocar',
- sortText = 'g',
- insertText = 'foodar(${1:var1})',
- insertTextFormat = 1,
- },
- {
- label = '•INT16_C(c)',
- insertText = 'INT16_C(${1:c})',
- insertTextFormat = 2,
- filterText = 'INT16_C',
- sortText = 'h',
- textEdit = {
- newText = 'INT16_C(${1:c})',
- range = range0,
- },
- },
- }
- local expected = {
- {
- abbr = 'foobar',
- word = 'foobar',
- },
- {
- abbr = 'foobar',
- word = 'foobar',
- },
- {
- abbr = 'foocar',
- word = 'foobar',
- },
- {
- abbr = 'foocar',
- word = 'foobar',
- },
- {
- abbr = 'foocar',
- word = 'foobar',
- },
- {
- abbr = 'foocar',
- word = 'foobar',
- },
- {
- abbr = 'foocar',
- word = 'foodar(${1:var1})', -- marked as PlainText, text is used as is
- },
- {
- abbr = '•INT16_C(c)',
- word = 'INT16_C',
- },
- }
- local result = complete('|', completion_list)
- result = vim.tbl_map(function(x)
- return {
- abbr = x.abbr,
- word = x.word,
- }
- end, result.items)
- eq(expected, result)
- end)
- it('does not filter if there is a textEdit', function()
- local range0 = {
- start = { line = 0, character = 0 },
- ['end'] = { line = 0, character = 0 },
- }
- local completion_list = {
- { label = 'foo', textEdit = { newText = 'foo', range = range0 } },
- { label = 'bar', textEdit = { newText = 'bar', range = range0 } },
- }
- local result = complete('fo|', completion_list)
- local expected = {
- {
- abbr = 'foo',
- word = 'foo',
- },
- {
- abbr = 'bar',
- word = 'bar',
- },
- }
- result = vim.tbl_map(function(x)
- return {
- abbr = x.abbr,
- word = x.word,
- }
- end, result.items)
- local sorter = function(a, b)
- return a.word > b.word
- end
- table.sort(expected, sorter)
- table.sort(result, sorter)
- eq(expected, result)
- end)
- ---@param prefix string
- ---@param items lsp.CompletionItem[]
- ---@param expected table[]
- local assert_completion_matches = function(prefix, items, expected)
- local result = complete(prefix .. '|', items)
- result = vim.tbl_map(function(x)
- return {
- abbr = x.abbr,
- word = x.word,
- }
- end, result.items)
- local sorter = function(a, b)
- return a.word > b.word
- end
- table.sort(expected, sorter)
- table.sort(result, sorter)
- eq(expected, result)
- end
- describe('when completeopt has fuzzy matching enabled', function()
- before_each(function()
- exec_lua(function()
- vim.opt.completeopt:append('fuzzy')
- end)
- end)
- after_each(function()
- exec_lua(function()
- vim.opt.completeopt:remove('fuzzy')
- end)
- end)
- it('fuzzy matches on filterText', function()
- assert_completion_matches('fo', {
- { label = '?.foo', filterText = 'foo' },
- { label = 'faz other', filterText = 'faz other' },
- { label = 'bar', filterText = 'bar' },
- }, {
- {
- abbr = 'faz other',
- word = 'faz other',
- },
- {
- abbr = '?.foo',
- word = '?.foo',
- },
- })
- end)
- it('fuzzy matches on label when filterText is missing', function()
- assert_completion_matches('fo', {
- { label = 'foo' },
- { label = 'faz other' },
- { label = 'bar' },
- }, {
- {
- abbr = 'faz other',
- word = 'faz other',
- },
- {
- abbr = 'foo',
- word = 'foo',
- },
- })
- end)
- end)
- describe('when smartcase is enabled', function()
- before_each(function()
- exec_lua(function()
- vim.opt.smartcase = true
- end)
- end)
- after_each(function()
- exec_lua(function()
- vim.opt.smartcase = false
- end)
- end)
- it('matches filterText case sensitively', function()
- assert_completion_matches('Fo', {
- { label = 'foo', filterText = 'foo' },
- { label = '?.Foo', filterText = 'Foo' },
- { label = 'Faz other', filterText = 'Faz other' },
- { label = 'faz other', filterText = 'faz other' },
- { label = 'bar', filterText = 'bar' },
- }, {
- {
- abbr = '?.Foo',
- word = '?.Foo',
- },
- })
- end)
- it('matches label case sensitively when filterText is missing', function()
- assert_completion_matches('Fo', {
- { label = 'foo' },
- { label = 'Foo' },
- { label = 'Faz other' },
- { label = 'faz other' },
- { label = 'bar' },
- }, {
- {
- abbr = 'Foo',
- word = 'Foo',
- },
- })
- end)
- describe('when ignorecase is enabled', function()
- before_each(function()
- exec_lua(function()
- vim.opt.ignorecase = true
- end)
- end)
- after_each(function()
- exec_lua(function()
- vim.opt.ignorecase = false
- end)
- end)
- it('matches filterText case insensitively if prefix is lowercase', function()
- assert_completion_matches('fo', {
- { label = '?.foo', filterText = 'foo' },
- { label = '?.Foo', filterText = 'Foo' },
- { label = 'Faz other', filterText = 'Faz other' },
- { label = 'faz other', filterText = 'faz other' },
- { label = 'bar', filterText = 'bar' },
- }, {
- {
- abbr = '?.Foo',
- word = '?.Foo',
- },
- {
- abbr = '?.foo',
- word = '?.foo',
- },
- })
- end)
- it(
- 'matches label case insensitively if prefix is lowercase and filterText is missing',
- function()
- assert_completion_matches('fo', {
- { label = 'foo' },
- { label = 'Foo' },
- { label = 'Faz other' },
- { label = 'faz other' },
- { label = 'bar' },
- }, {
- {
- abbr = 'Foo',
- word = 'Foo',
- },
- {
- abbr = 'foo',
- word = 'foo',
- },
- })
- end
- )
- it('matches filterText case sensitively if prefix has uppercase letters', function()
- assert_completion_matches('Fo', {
- { label = 'foo', filterText = 'foo' },
- { label = '?.Foo', filterText = 'Foo' },
- { label = 'Faz other', filterText = 'Faz other' },
- { label = 'faz other', filterText = 'faz other' },
- { label = 'bar', filterText = 'bar' },
- }, {
- {
- abbr = '?.Foo',
- word = '?.Foo',
- },
- })
- end)
- it(
- 'matches label case sensitively if prefix has uppercase letters and filterText is missing',
- function()
- assert_completion_matches('Fo', {
- { label = 'foo' },
- { label = 'Foo' },
- { label = 'Faz other' },
- { label = 'faz other' },
- { label = 'bar' },
- }, {
- {
- abbr = 'Foo',
- word = 'Foo',
- },
- })
- end
- )
- end)
- end)
- describe('when ignorecase is enabled', function()
- before_each(function()
- exec_lua(function()
- vim.opt.ignorecase = true
- end)
- end)
- after_each(function()
- exec_lua(function()
- vim.opt.ignorecase = false
- end)
- end)
- it('matches filterText case insensitively', function()
- assert_completion_matches('Fo', {
- { label = '?.foo', filterText = 'foo' },
- { label = '?.Foo', filterText = 'Foo' },
- { label = 'Faz other', filterText = 'Faz other' },
- { label = 'faz other', filterText = 'faz other' },
- { label = 'bar', filterText = 'bar' },
- }, {
- {
- abbr = '?.Foo',
- word = '?.Foo',
- },
- {
- abbr = '?.foo',
- word = '?.foo',
- },
- })
- end)
- it('matches label case insensitively when filterText is missing', function()
- assert_completion_matches('Fo', {
- { label = 'foo' },
- { label = 'Foo' },
- { label = 'Faz other' },
- { label = 'faz other' },
- { label = 'bar' },
- }, {
- {
- abbr = 'Foo',
- word = 'Foo',
- },
- {
- abbr = 'foo',
- word = 'foo',
- },
- })
- end)
- end)
- it('works on non word prefix', function()
- local completion_list = {
- { label = ' foo', insertText = '->foo' },
- }
- local result = complete('wp.|', completion_list, 0, 2)
- local expected = {
- {
- abbr = ' foo',
- word = '->foo',
- },
- }
- result = vim.tbl_map(function(x)
- return {
- abbr = x.abbr,
- word = x.word,
- }
- end, result.items)
- eq(expected, result)
- end)
- it('trims trailing newline or tab from textEdit', function()
- local range0 = {
- start = { line = 0, character = 0 },
- ['end'] = { line = 0, character = 0 },
- }
- local items = {
- {
- detail = 'ansible.builtin',
- filterText = 'lineinfile ansible.builtin.lineinfile builtin ansible',
- kind = 7,
- label = 'ansible.builtin.lineinfile',
- sortText = '2_ansible.builtin.lineinfile',
- textEdit = {
- newText = 'ansible.builtin.lineinfile:\n ',
- range = range0,
- },
- },
- }
- local result = complete('|', items)
- result = vim.tbl_map(function(x)
- return {
- abbr = x.abbr,
- word = x.word,
- }
- end, result.items)
- local expected = {
- {
- abbr = 'ansible.builtin.lineinfile',
- word = 'ansible.builtin.lineinfile:',
- },
- }
- eq(expected, result)
- end)
- it('prefers wordlike components for snippets', function()
- -- There are two goals here:
- --
- -- 1. The `word` should match what the user started typing, so that vim.fn.complete() doesn't
- -- filter it away, preventing snippet expansion
- --
- -- For example, if they type `items@ins`, luals returns `table.insert(items, $0)` as
- -- textEdit.newText and `insert` as label.
- -- There would be no prefix match if textEdit.newText is used as `word`
- --
- -- 2. If users do not expand a snippet, but continue typing, they should see a somewhat reasonable
- -- `word` getting inserted.
- --
- -- For example in:
- --
- -- insertText: "testSuites ${1:Env}"
- -- label: "testSuites"
- --
- -- "testSuites" should have priority as `word`, as long as the full snippet gets expanded on accept (<c-y>)
- local range0 = {
- start = { line = 0, character = 0 },
- ['end'] = { line = 0, character = 0 },
- }
- local completion_list = {
- -- luals postfix snippet (typed text: items@ins|)
- {
- label = 'insert',
- insertTextFormat = 2,
- textEdit = {
- newText = 'table.insert(items, $0)',
- range = range0,
- },
- },
- -- eclipse.jdt.ls `new` snippet
- {
- label = 'new',
- insertTextFormat = 2,
- textEdit = {
- newText = '${1:Object} ${2:foo} = new ${1}(${3});\n${0}',
- range = range0,
- },
- textEditText = '${1:Object} ${2:foo} = new ${1}(${3});\n${0}',
- },
- -- eclipse.jdt.ls `List.copyO` function call completion
- {
- label = 'copyOf(Collection<? extends E> coll) : List<E>',
- insertTextFormat = 2,
- insertText = 'copyOf',
- textEdit = {
- newText = 'copyOf(${1:coll})',
- range = range0,
- },
- },
- }
- local expected = {
- {
- abbr = 'copyOf(Collection<? extends E> coll) : List<E>',
- word = 'copyOf',
- },
- {
- abbr = 'insert',
- word = 'insert',
- },
- {
- abbr = 'new',
- word = 'new',
- },
- }
- local result = complete('|', completion_list)
- result = vim.tbl_map(function(x)
- return {
- abbr = x.abbr,
- word = x.word,
- }
- end, result.items)
- eq(expected, result)
- end)
- it('uses correct start boundary', function()
- local completion_list = {
- isIncomplete = false,
- items = {
- {
- filterText = 'this_thread',
- insertText = 'this_thread',
- insertTextFormat = 1,
- kind = 9,
- label = ' this_thread',
- score = 1.3205767869949,
- sortText = '4056f757this_thread',
- textEdit = {
- newText = 'this_thread',
- range = {
- start = { line = 0, character = 7 },
- ['end'] = { line = 0, character = 11 },
- },
- },
- },
- },
- }
- local expected = {
- abbr = ' this_thread',
- dup = 1,
- empty = 1,
- icase = 1,
- info = '',
- kind = 'Module',
- menu = '',
- abbr_hlgroup = '',
- word = 'this_thread',
- }
- local result = complete(' std::this|', completion_list)
- eq(7, result.server_start_boundary)
- local item = result.items[1]
- item.user_data = nil
- eq(expected, item)
- end)
- it('should search from start boundary to cursor position', function()
- local completion_list = {
- isIncomplete = false,
- items = {
- {
- filterText = 'this_thread',
- insertText = 'this_thread',
- insertTextFormat = 1,
- kind = 9,
- label = ' this_thread',
- score = 1.3205767869949,
- sortText = '4056f757this_thread',
- textEdit = {
- newText = 'this_thread',
- range = {
- start = { line = 0, character = 7 },
- ['end'] = { line = 0, character = 11 },
- },
- },
- },
- {
- filterText = 'no_match',
- insertText = 'notthis_thread',
- insertTextFormat = 1,
- kind = 9,
- label = ' notthis_thread',
- score = 1.3205767869949,
- sortText = '4056f757this_thread',
- textEdit = {
- newText = 'notthis_thread',
- range = {
- start = { line = 0, character = 7 },
- ['end'] = { line = 0, character = 11 },
- },
- },
- },
- },
- }
- local expected = {
- abbr = ' this_thread',
- dup = 1,
- empty = 1,
- icase = 1,
- info = '',
- kind = 'Module',
- menu = '',
- abbr_hlgroup = '',
- word = 'this_thread',
- }
- local result = complete(' std::this|is', completion_list)
- eq(1, #result.items)
- local item = result.items[1]
- item.user_data = nil
- eq(expected, item)
- end)
- it('uses defaults from itemDefaults', function()
- --- @type lsp.CompletionList
- local completion_list = {
- isIncomplete = false,
- itemDefaults = {
- editRange = {
- start = { line = 1, character = 1 },
- ['end'] = { line = 1, character = 4 },
- },
- insertTextFormat = 2,
- data = 'foobar',
- },
- items = {
- {
- label = 'hello',
- data = 'item-property-has-priority',
- textEditText = 'hello',
- },
- },
- }
- local result = complete('|', completion_list)
- eq(1, #result.items)
- local item = result.items[1].user_data.nvim.lsp.completion_item --- @type lsp.CompletionItem
- eq(2, item.insertTextFormat)
- eq('item-property-has-priority', item.data)
- eq({ line = 1, character = 1 }, item.textEdit.range.start)
- end)
- it(
- 'uses insertText as textEdit.newText if there are editRange defaults but no textEditText',
- function()
- --- @type lsp.CompletionList
- local completion_list = {
- isIncomplete = false,
- itemDefaults = {
- editRange = {
- start = { line = 1, character = 1 },
- ['end'] = { line = 1, character = 4 },
- },
- insertTextFormat = 2,
- data = 'foobar',
- },
- items = {
- {
- insertText = 'the-insertText',
- label = 'hello',
- data = 'item-property-has-priority',
- },
- },
- }
- local result = complete('|', completion_list)
- eq(1, #result.items)
- local text = result.items[1].user_data.nvim.lsp.completion_item.textEdit.newText
- eq('the-insertText', text)
- end
- )
- it(
- 'defaults to label as textEdit.newText if insertText or textEditText are not present',
- function()
- local completion_list = {
- isIncomplete = false,
- itemDefaults = {
- editRange = {
- start = { line = 1, character = 1 },
- ['end'] = { line = 1, character = 4 },
- },
- insertTextFormat = 2,
- data = 'foobar',
- },
- items = {
- {
- label = 'hello',
- data = 'item-property-has-priority',
- },
- },
- }
- local result = complete('|', completion_list)
- eq(1, #result.items)
- local text = result.items[1].user_data.nvim.lsp.completion_item.textEdit.newText
- eq('hello', text)
- end
- )
- end)
- --- @param name string
- --- @param completion_result lsp.CompletionList
- --- @return integer
- local function create_server(name, completion_result)
- return exec_lua(function()
- local server = _G._create_server({
- capabilities = {
- completionProvider = {
- triggerCharacters = { '.' },
- },
- },
- handlers = {
- ['textDocument/completion'] = function(_, _, callback)
- callback(nil, completion_result)
- end,
- },
- })
- local bufnr = vim.api.nvim_get_current_buf()
- vim.api.nvim_win_set_buf(0, bufnr)
- return vim.lsp.start({
- name = name,
- cmd = server.cmd,
- on_attach = function(client, bufnr0)
- vim.lsp.completion.enable(true, client.id, bufnr0, {
- convert = function(item)
- return { abbr = item.label:gsub('%b()', '') }
- end,
- })
- end,
- })
- end)
- end
- describe('vim.lsp.completion: protocol', function()
- before_each(function()
- clear()
- exec_lua(create_server_definition)
- exec_lua(function()
- _G.capture = {}
- --- @diagnostic disable-next-line:duplicate-set-field
- vim.fn.complete = function(col, matches)
- _G.capture.col = col
- _G.capture.matches = matches
- end
- end)
- end)
- after_each(clear)
- local function assert_matches(fn)
- retry(nil, nil, function()
- fn(exec_lua('return _G.capture.matches'))
- end)
- end
- --- @param pos [integer, integer]
- local function trigger_at_pos(pos)
- exec_lua(function()
- local win = vim.api.nvim_get_current_win()
- vim.api.nvim_win_set_cursor(win, pos)
- vim.lsp.completion.trigger()
- end)
- retry(nil, nil, function()
- neq(nil, exec_lua('return _G.capture.col'))
- end)
- end
- it('fetches completions and shows them using complete on trigger', function()
- create_server('dummy', {
- isIncomplete = false,
- items = {
- {
- label = 'hello',
- },
- {
- label = 'hercules',
- tags = { 1 }, -- 1 represents Deprecated tag
- },
- {
- label = 'hero',
- deprecated = true,
- },
- },
- })
- feed('ih')
- trigger_at_pos({ 1, 1 })
- assert_matches(function(matches)
- eq({
- {
- abbr = 'hello',
- dup = 1,
- empty = 1,
- icase = 1,
- info = '',
- kind = 'Unknown',
- menu = '',
- abbr_hlgroup = '',
- user_data = {
- nvim = {
- lsp = {
- client_id = 1,
- completion_item = {
- label = 'hello',
- },
- },
- },
- },
- word = 'hello',
- },
- {
- abbr = 'hercules',
- dup = 1,
- empty = 1,
- icase = 1,
- info = '',
- kind = 'Unknown',
- menu = '',
- abbr_hlgroup = 'DiagnosticDeprecated',
- user_data = {
- nvim = {
- lsp = {
- client_id = 1,
- completion_item = {
- label = 'hercules',
- tags = { 1 },
- },
- },
- },
- },
- word = 'hercules',
- },
- {
- abbr = 'hero',
- dup = 1,
- empty = 1,
- icase = 1,
- info = '',
- kind = 'Unknown',
- menu = '',
- abbr_hlgroup = 'DiagnosticDeprecated',
- user_data = {
- nvim = {
- lsp = {
- client_id = 1,
- completion_item = {
- label = 'hero',
- deprecated = true,
- },
- },
- },
- },
- word = 'hero',
- },
- }, matches)
- end)
- end)
- it('merges results from multiple clients', function()
- create_server('dummy1', {
- isIncomplete = false,
- items = {
- {
- label = 'hello',
- },
- },
- })
- create_server('dummy2', {
- isIncomplete = false,
- items = {
- {
- label = 'hallo',
- },
- },
- })
- feed('ih')
- trigger_at_pos({ 1, 1 })
- assert_matches(function(matches)
- eq(2, #matches)
- eq('hello', matches[1].word)
- eq('hallo', matches[2].word)
- end)
- end)
- it('executes commands', function()
- local completion_list = {
- isIncomplete = false,
- items = {
- {
- label = 'hello',
- command = {
- arguments = { '1', '0' },
- command = 'dummy',
- title = '',
- },
- },
- },
- }
- local client_id = create_server('dummy', completion_list)
- exec_lua(function()
- _G.called = false
- local client = assert(vim.lsp.get_client_by_id(client_id))
- client.commands.dummy = function()
- _G.called = true
- end
- end)
- feed('ih')
- trigger_at_pos({ 1, 1 })
- local item = completion_list.items[1]
- exec_lua(function()
- vim.v.completed_item = {
- user_data = {
- nvim = {
- lsp = {
- client_id = client_id,
- completion_item = item,
- },
- },
- },
- }
- end)
- feed('<C-x><C-o><C-y>')
- assert_matches(function(matches)
- eq(1, #matches)
- eq('hello', matches[1].word)
- eq(true, exec_lua('return _G.called'))
- end)
- end)
- it('enable(…,{convert=fn}) custom word/abbr format', function()
- create_server('dummy', {
- isIncomplete = false,
- items = {
- {
- label = 'foo(bar)',
- },
- },
- })
- feed('ifo')
- trigger_at_pos({ 1, 1 })
- assert_matches(function(matches)
- eq('foo', matches[1].abbr)
- end)
- end)
- end)
- describe('vim.lsp.completion: integration', function()
- before_each(function()
- clear()
- exec_lua(create_server_definition)
- exec_lua(function()
- vim.fn.complete = vim.schedule_wrap(vim.fn.complete)
- end)
- end)
- after_each(clear)
- it('puts cursor at the end of completed word', function()
- local completion_list = {
- isIncomplete = false,
- items = {
- {
- label = 'hello',
- insertText = '${1:hello} friends',
- insertTextFormat = 2,
- },
- },
- }
- exec_lua(function()
- vim.o.completeopt = 'menuone,noselect'
- end)
- create_server('dummy', completion_list)
- feed('i world<esc>0ih<c-x><c-o>')
- retry(nil, nil, function()
- eq(
- 1,
- exec_lua(function()
- return vim.fn.pumvisible()
- end)
- )
- end)
- feed('<C-n><C-y>')
- eq(
- { true, { 'hello friends world' } },
- exec_lua(function()
- return {
- vim.snippet.active({ direction = 1 }),
- vim.api.nvim_buf_get_lines(0, 0, -1, true),
- }
- end)
- )
- feed('<tab>')
- eq(
- #'hello friends',
- exec_lua(function()
- return vim.api.nvim_win_get_cursor(0)[2]
- end)
- )
- end)
- end)
|