diff options
Diffstat (limited to 'start/cmp/lua/cmp_nvim_lsp')
-rw-r--r-- | start/cmp/lua/cmp_nvim_lsp/init.lua | 84 | ||||
-rw-r--r-- | start/cmp/lua/cmp_nvim_lsp/source.lua | 117 |
2 files changed, 201 insertions, 0 deletions
diff --git a/start/cmp/lua/cmp_nvim_lsp/init.lua b/start/cmp/lua/cmp_nvim_lsp/init.lua new file mode 100644 index 0000000..9feb93a --- /dev/null +++ b/start/cmp/lua/cmp_nvim_lsp/init.lua @@ -0,0 +1,84 @@ +local source = require('cmp_nvim_lsp.source') + +local M = {} + +---Registered client and source mapping. +M.client_source_map = {} + +---Setup cmp-nvim-lsp source. +M.setup = function() + vim.cmd([[ + augroup cmp_nvim_lsp + autocmd! + autocmd InsertEnter * lua require'cmp_nvim_lsp'._on_insert_enter() + augroup END + ]]) +end + +local if_nil = function(val, default) + if val == nil then return default end + return val +end + +M.update_capabilities = function(capabilities, override) + override = override or {} + + local completionItem = capabilities.textDocument.completion.completionItem + + completionItem.snippetSupport = if_nil(override.snippetSupport, true) + completionItem.preselectSupport = if_nil(override.preselectSupport, true) + completionItem.insertReplaceSupport = if_nil(override.insertReplaceSupport, true) + completionItem.labelDetailsSupport = if_nil(override.labelDetailsSupport, true) + completionItem.deprecatedSupport = if_nil(override.deprecatedSupport, true) + completionItem.commitCharactersSupport = if_nil(override.commitCharactersSupport, true) + completionItem.tagSupport = if_nil(override.tagSupport, { valueSet = { 1 } }) + completionItem.resolveSupport = if_nil(override.resolveSupport, { + properties = { + 'documentation', + 'detail', + 'additionalTextEdits', + } + }) + + return capabilities +end + +---Refresh sources on InsertEnter. +M._on_insert_enter = function() + local cmp = require('cmp') + + local allowed_clients = {} + + -- register all active clients. + for _, client in ipairs(vim.lsp.get_active_clients()) do + allowed_clients[client.id] = client + if not M.client_source_map[client.id] then + local s = source.new(client) + if s:is_available() then + M.client_source_map[client.id] = cmp.register_source('nvim_lsp', s) + end + end + end + + -- register all buffer clients (early register before activation) + for _, client in ipairs(vim.lsp.buf_get_clients(0)) do + allowed_clients[client.id] = client + if not M.client_source_map[client.id] then + local s = source.new(client) + if s:is_available() then + M.client_source_map[client.id] = cmp.register_source('nvim_lsp', s) + end + end + end + + -- unregister stopped/detached clients. + for client_id, source_id in pairs(M.client_source_map) do + if not allowed_clients[client_id] or allowed_clients[client_id]:is_stopped() then + cmp.unregister_source(source_id) + M.client_source_map[client_id] = nil + end + end +end + +return M + diff --git a/start/cmp/lua/cmp_nvim_lsp/source.lua b/start/cmp/lua/cmp_nvim_lsp/source.lua new file mode 100644 index 0000000..d4dd587 --- /dev/null +++ b/start/cmp/lua/cmp_nvim_lsp/source.lua @@ -0,0 +1,117 @@ +local source = {} + +source.new = function(client) + local self = setmetatable({}, { __index = source }) + self.client = client + self.request_ids = {} + return self +end + +source.get_debug_name = function(self) + return table.concat({ 'nvim_lsp', self.client.name }, ':') +end + +source.is_available = function(self) + -- client is stopped. + if self.client.is_stopped() then + return false + end + + -- client is not attached to current buffer. + if not vim.lsp.buf_get_clients(vim.api.nvim_get_current_buf())[self.client.id] then + return false + end + + -- client has no completion capability. + if not self:_get(self.client.server_capabilities, { 'completionProvider' }) then + return false + end + return true; +end + +source.get_trigger_characters = function(self) + return self:_get(self.client.server_capabilities, { 'completionProvider', 'triggerCharacters' }) or {} +end + +source.complete = function(self, request, callback) + local params = vim.lsp.util.make_position_params(0, self.client.offset_encoding) + params.context = {} + params.context.triggerKind = request.completion_context.triggerKind + params.context.triggerCharacter = request.completion_context.triggerCharacter + + self:_request('textDocument/completion', params, function(_, response) + callback(response) + end) +end + +source.resolve = function(self, completion_item, callback) + -- client is stopped. + if self.client.is_stopped() then + return callback() + end + + -- client has no completion capability. + if not self:_get(self.client.server_capabilities, { 'completionProvider', 'resolveProvider' }) then + return callback() + end + + self:_request('completionItem/resolve', completion_item, function(_, response) + callback(response or completion_item) + end) +end + +source.execute = function(self, completion_item, callback) + -- client is stopped. + if self.client.is_stopped() then + return callback() + end + + -- completion_item has no command. + if not completion_item.command then + return callback() + end + + self:_request('workspace/executeCommand', completion_item.command, function(_, _) + callback() + end) +end + +source._get = function(_, root, paths) + local c = root + for _, path in ipairs(paths) do + c = c[path] + if not c then + return nil + end + end + return c +end + +source._request = function(self, method, params, callback) + if self.request_ids[method] ~= nil then + self.client.cancel_request(self.request_ids[method]) + self.request_ids[method] = nil + end + local _, request_id + _, request_id = self.client.request(method, params, function(arg1, arg2, arg3) + if self.request_ids[method] ~= request_id then + return + end + self.request_ids[method] = nil + + -- Text changed, retry + if arg1 and arg1.code == -32801 then + self:_request(method, params, callback) + return + end + + if method == arg2 then + callback(arg1, arg3) -- old signature + else + callback(arg1, arg2) -- new signature + end + end) + self.request_ids[method] = request_id +end + +return source |