1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
local async = require('blink.cmp.lib.async')
--- @type blink.cmp.Source
--- @diagnostic disable-next-line: missing-fields
local lsp = {}
function lsp.new() return setmetatable({}, { __index = lsp }) end
--- Completion ---
function lsp:get_trigger_characters()
local clients = vim.lsp.get_clients({ bufnr = 0 })
local trigger_characters = {}
for _, client in pairs(clients) do
local completion_provider = client.server_capabilities.completionProvider
if completion_provider and completion_provider.triggerCharacters then
for _, trigger_character in pairs(completion_provider.triggerCharacters) do
table.insert(trigger_characters, trigger_character)
end
end
end
return trigger_characters
end
function lsp:get_completions(context, callback)
local completion_lib = require('blink.cmp.sources.lsp.completion')
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' })
)
-- 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))
:map(function(responses)
local final = { is_incomplete_forward = false, is_incomplete_backward = false, items = {} }
for _, response in ipairs(responses) do
final.is_incomplete_forward = final.is_incomplete_forward or response.is_incomplete_forward
final.is_incomplete_backward = final.is_incomplete_backward or response.is_incomplete_backward
vim.list_extend(final.items, response.items)
end
callback(final)
end)
return function() task:cancel() end
end
--- Resolve ---
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
callback(item)
return
end
-- strip blink specific fields to avoid decoding errors on some LSPs
item = require('blink.cmp.sources.lib.utils').blink_item_to_lsp_item(item)
local success, request_id = client.request('completionItem/resolve', item, function(error, resolved_item)
if error or resolved_item == nil then callback(item) end
callback(resolved_item)
end)
if not success then callback(item) end
if request_id ~= nil then
return function() client.cancel_request(request_id) end
end
end
--- Signature help ---
function lsp:get_signature_help_trigger_characters()
local clients = vim.lsp.get_clients({ bufnr = 0 })
local trigger_characters = {}
local retrigger_characters = {}
for _, client in pairs(clients) do
local signature_help_provider = client.server_capabilities.signatureHelpProvider
if signature_help_provider and signature_help_provider.triggerCharacters then
for _, trigger_character in pairs(signature_help_provider.triggerCharacters) do
table.insert(trigger_characters, trigger_character)
end
end
if signature_help_provider and signature_help_provider.retriggerCharacters then
for _, retrigger_character in pairs(signature_help_provider.retriggerCharacters) do
table.insert(retrigger_characters, retrigger_character)
end
end
end
return { trigger_characters = trigger_characters, retrigger_characters = retrigger_characters }
end
function lsp:get_signature_help(context, callback)
-- no providers with signature help support
if #vim.lsp.get_clients({ bufnr = 0, method = 'textDocument/signatureHelp' }) == 0 then
callback(nil)
return function() end
end
-- TODO: offset encoding is global but should be per-client
local first_client = vim.lsp.get_clients({ bufnr = 0 })[1]
local offset_encoding = first_client and first_client.offset_encoding or 'utf-16'
local params = vim.lsp.util.make_position_params(nil, offset_encoding)
params.context = {
triggerKind = context.trigger.kind,
triggerCharacter = context.trigger.character,
isRetrigger = context.is_retrigger,
activeSignatureHelp = context.active_signature_help,
}
-- otherwise, we call all clients
-- TODO: some LSPs never response (typescript-tools.nvim)
return vim.lsp.buf_request_all(0, 'textDocument/signatureHelp', params, function(result)
local signature_helps = {}
for client_id, res in pairs(result) do
local signature_help = res.result
if signature_help ~= nil then
signature_help.client_id = client_id
table.insert(signature_helps, signature_help)
end
end
-- todo: pick intelligently
callback(signature_helps[1])
end)
end
return lsp
|