189 lines
5.3 KiB
Lua
189 lines
5.3 KiB
Lua
-- My neovim autocomplete setup.
|
|
-- partially stolen from https://github.com/glepnir/nvim
|
|
--
|
|
-- To make this work nicely you're gonna want a solid snippet setup, for me this
|
|
-- is luasnip. If you too would like to use luasnip just set vim.snippet.expand
|
|
-- to luasnip.lsp_expand
|
|
|
|
--- add char to list of triggerable chars
|
|
---@param list table list of chars
|
|
---@param char number|string char to add
|
|
local function add_to_client(list, char)
|
|
local c
|
|
|
|
if type(char) == "string" then
|
|
c = char
|
|
elseif type(char) == "number" then
|
|
c = string.char(char)
|
|
end
|
|
if not table.contains(list, c) then
|
|
table.insert(list, c)
|
|
end
|
|
end
|
|
|
|
-- make sure we can add autos
|
|
local misc = require("core.misc")
|
|
local auto = misc.auto
|
|
local completion_group = misc.augroup("lsp.completion")
|
|
|
|
-- Configure the lsp completion menu. In addition to setting up lsp completion
|
|
-- this also styles it to look nice.
|
|
auto("LspAttach", {
|
|
group = completion_group,
|
|
callback = function(ctx)
|
|
local client = vim.lsp.get_client_by_id(ctx.data.client_id)
|
|
if not client or not client:supports_method("textDocument/completion") then
|
|
return
|
|
end
|
|
|
|
-- Make completion menu appear whenever you type something.
|
|
local c = client.server_capabilities.completionProvider.triggerCharacters
|
|
if c then
|
|
for i = 32, 126 do
|
|
add_to_client(c, string.char(i))
|
|
end
|
|
end
|
|
|
|
vim.lsp.completion.enable(true, client.id, ctx.buf, {
|
|
autotrigger = true,
|
|
convert = function(item)
|
|
local kind = vim.lsp.protocol.CompletionItemKind[item.kind] or 'u'
|
|
return {
|
|
-- in the future if we ever get the ability to highlight specific
|
|
-- entries in the pummenu I'd like to add treesitter highlighting to
|
|
-- the entries
|
|
abbr = item.label:gsub('%b()', ''),
|
|
kind = kind:sub(1, 1):lower(),
|
|
menu = ''
|
|
}
|
|
end
|
|
})
|
|
end
|
|
})
|
|
|
|
-- attempt to style the completion documentation popup
|
|
auto("CompleteChanged", {
|
|
group = completion_group,
|
|
callback = function()
|
|
local info = vim.fn.complete_info({ "selected" })
|
|
if info.preview_bufnr and vim.bo[info.preview_bufnr].filetype == "" then
|
|
vim.bo[info.preview_bufnr].filetype = "markdown"
|
|
vim.wo[info.preview_winid].conceallevel = 2
|
|
vim.wo[info.preview_winid].concealcursor = "niv"
|
|
end
|
|
end
|
|
})
|
|
|
|
-- Add snippet support to lsp clients who don't do it for us. Currently this
|
|
-- only supports function calls.
|
|
--
|
|
-- there's a very good chance this will give us problems for languages who do
|
|
-- not follow the c-style function call style. I've only gotten this working in
|
|
-- lua thus far.
|
|
auto("CompleteDonePre", {
|
|
group = completion_group,
|
|
callback = function()
|
|
local item = vim.tbl_get(
|
|
vim.v.completed_item,
|
|
"user_data",
|
|
"nvim",
|
|
"lsp",
|
|
"completion_item"
|
|
)
|
|
if not item then
|
|
return
|
|
end
|
|
|
|
-- if the item isn't a snippet then we might want to create a snippet
|
|
if item.label and item.kind and item.insertTextFormat then
|
|
if item.insertTextFormat ~= 2 and item.kind == 3 then
|
|
local n_complete_item = vim.v.completed_item
|
|
|
|
-- attempt to modify the function args to create a snippet
|
|
local paren1 = string.find(item.label, "%(")
|
|
if not paren1 then
|
|
return
|
|
end
|
|
|
|
-- try and create a snippet from a function call
|
|
local i = 1
|
|
local text = ""
|
|
local l_paren = paren1
|
|
local next, is_paren
|
|
while not is_paren do
|
|
-- find the next token
|
|
next = string.find(item.label, "%,", l_paren + 1)
|
|
if not next then
|
|
next = string.find(item.label, "%)", l_paren + 1)
|
|
if not next then
|
|
return
|
|
end
|
|
is_paren = true
|
|
end
|
|
|
|
-- concat text
|
|
if text == "" then
|
|
-- start the snippet
|
|
text = string.sub(item.label, 0, l_paren).."${"..i..":"
|
|
else
|
|
-- continue the snippet
|
|
text = text.."}, ".."${"..i..":"
|
|
end
|
|
|
|
do -- add the content of the argument to the snippet
|
|
-- we need to account for cases in which the developer uses spaces
|
|
-- between their commas and code
|
|
local is_space = string.sub(item.label, l_paren + 1, l_paren + 1)
|
|
local plus = is_space == " " and 2 or 1
|
|
|
|
text = text..string.sub(item.label, l_paren + plus, next - 1)
|
|
end
|
|
|
|
-- increment
|
|
l_paren = next
|
|
i = i + 1
|
|
end
|
|
-- end the snippet
|
|
text = text.."})"
|
|
|
|
n_complete_item.user_data.nvim.lsp.completion_item.insertText = text
|
|
n_complete_item.user_data.nvim.lsp.completion_item.insertTextFormat = 2
|
|
n_complete_item.user_data.nvim.lsp.completion_item.kind = 3
|
|
vim.v.completed_item = n_complete_item
|
|
end
|
|
end
|
|
end
|
|
})
|
|
|
|
-- show the signature help when inside a function call
|
|
auto("CompleteDone", {
|
|
group = completion_group,
|
|
callback = function(ctx)
|
|
-- make sure there's an lsp client, and make sure the lsp client supports
|
|
-- textDocument/signatureHelp
|
|
local client = vim.lsp.get_clients({ bufnr = ctx.buf })[1]
|
|
if not client or not client:supports_method("textDocument/signatureHelp") then
|
|
return
|
|
end
|
|
|
|
-- show the signature help
|
|
local item = vim.tbl_get(
|
|
vim.v.completed_item,
|
|
"user_data",
|
|
"nvim",
|
|
"lsp",
|
|
"completion_item"
|
|
)
|
|
if not item then
|
|
return
|
|
end
|
|
|
|
-- show signature help when the completion is a function
|
|
if item.kind == 3 and (item.textEdit ~= nil or item.insertText ~= nil) then
|
|
-- for some reason calling vim.schedule didn't work for me
|
|
vim.defer_fn(vim.lsp.buf.signature_help, 100)
|
|
end
|
|
end,
|
|
desc = "Auto show signature help when completion is done"
|
|
})
|