Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions lua/blink/cmp/sources/lsp/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,29 @@ end

function lsp:get_completions(context, callback)
local completion_lib = require('blink.cmp.sources.lsp.completion')
local inlinecompletion_lib = require('blink.cmp.sources.lsp.inlinecompletion')
local clients = vim.tbl_filter(
function(client) return client.server_capabilities and client.server_capabilities.completionProvider end,
vim.lsp.get_clients({ bufnr = 0, method = 'textDocument/completion' })
)

local inline_clients = vim.tbl_filter(
function(client) return client.server_capabilities and client.server_capabilities.inlineCompletionProvider end,
vim.lsp.get_clients({ bufnr = 0, method = 'textDocument/inlineCompletion' })
)

-- TODO: implement a timeout before returning the menu as-is. In the future, it would be neat
-- to detect slow LSPs and consistently run them async
local task = async.task
.await_all(vim.tbl_map(function(client) return completion_lib.get_completion_for_client(context, client) end, clients))
.await_all(
vim.list_extend(
vim.tbl_map(function(client) return completion_lib.get_completion_for_client(context, client) end, clients),
vim.tbl_map(
function(client) return inlinecompletion_lib.get_inlinecompletion_for_client(context, client) end,
inline_clients
)
)
)
:map(function(responses)
local final = { is_incomplete_forward = false, is_incomplete_backward = false, items = {} }
for _, response in ipairs(responses) do
Expand All @@ -58,7 +72,9 @@ end

function lsp:resolve(item, callback)
local client = vim.lsp.get_client_by_id(item.client_id)
if client == nil or not client.server_capabilities.completionProvider.resolveProvider then
local resolveProvider = client.server_capabilities.completionProvider
and client.server_capabilities.completionProvider.resolveProvider
if client == nil or not resolveProvider or item.inline then
callback(item)
return
end
Expand Down
63 changes: 63 additions & 0 deletions lua/blink/cmp/sources/lsp/inlinecompletion.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
local async = require('blink.cmp.lib.async')

-- How a completion was triggered
local InlineCompletionTriggerKind = {
Invoked = 1,
Automatic = 2,
}

local inline_completion = {}

--- @param context blink.cmp.Context
--- @param client vim.lsp.Client
--- @return blink.cmp.Task
function inline_completion.get_inlinecompletion_for_client(context, client)
return async.task.new(function(resolve)
local params = vim.lsp.util.make_position_params(0, client.offset_encoding)
params.context = {
triggerKind = context.trigger.kind == 'Invoked' and InlineCompletionTriggerKind.Invoked
or InlineCompletionTriggerKind.Automatic,
}

params.formattingOptions = {
tabSize = vim.fn.shiftwidth(),
insertSpaces = vim.o.expandtab,
}

local _, request_id = client.request('textDocument/inlineCompletion', params, function(err, result)
if err or result == nil then
resolve({ is_incomplete_forward = true, is_incomplete_backward = true, items = {} })
return
end

local items = result.items or result
for _, item in ipairs(items) do
item.client_id = client.id
item.client_name = client.name
item.documentation = item.insertText
item.label = item.insertText
item.kind = require('blink.cmp.types').CompletionItemKind.Snippet
item.inline = true

-- convert to traditional TextEdit
item.textEdit = {
newText = item.insertText,
range = item.range,
}
end

resolve({
is_incomplete_forward = false,
is_incomplete_backward = false,
items = items,
})
end)

-- cancellation function
return function()
if request_id ~= nil then client.cancel_request(request_id) end
end
end)
end

return inline_completion