vim.opt.number = true
vim.opt.signcolumn = "yes"
vim.opt.updatetime = 300
vim.opt.completeopt = { "menuone", "noselect", "popup" }
vim.opt.pumheight = 15
local lsp_bin = vim.fn.fnamemodify("target/release/perl-lsp", ":p")
local debug_mode = vim.env.PERL_LSP_DEBUG == "1"
local log_level = debug_mode and "debug" or "warn"
local log_file = "/tmp/perl-lsp.log"
local cmd
if debug_mode then
cmd = {
"sh", "-c",
"RUST_LOG=perl_lsp=" .. log_level .. " exec " .. vim.fn.shellescape(lsp_bin) .. " 2>>" .. log_file,
}
else
cmd = { lsp_bin }
end
vim.lsp.config["perl-lsp"] = {
cmd = cmd,
filetypes = { "perl" },
root_markers = { ".git", "Makefile", "cpanfile", "Makefile.PL", "Build.PL" },
}
vim.lsp.enable("perl-lsp")
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local buf = args.buf
local client_id = args.data.client_id
local opts = { buffer = buf }
vim.lsp.completion.enable(true, client_id, buf, { autotrigger = true })
vim.lsp.inlay_hint.enable(true, { bufnr = buf })
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
vim.keymap.set("n", "<leader>o", vim.lsp.buf.document_symbol, opts)
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
buffer = buf,
callback = vim.lsp.buf.document_highlight,
})
vim.api.nvim_create_autocmd("CursorMoved", {
buffer = buf,
callback = vim.lsp.buf.clear_references,
})
local sel_stack = {}
local function clamp(lnum, col)
local last_line = vim.api.nvim_buf_line_count(buf)
lnum = math.max(1, math.min(lnum, last_line))
local line_text = vim.api.nvim_buf_get_lines(buf, lnum - 1, lnum, false)[1] or ""
col = math.max(0, math.min(col, math.max(0, #line_text - 1)))
return lnum, col
end
local function flatten_sr(node)
local ranges = {}
while node do
local r = node.range
table.insert(ranges, r)
node = node.parent
end
return ranges
end
local function set_visual(r)
local sl, sc = clamp(r.start.line + 1, r.start.character)
local el, ec = clamp(r["end"].line + 1, math.max(0, r["end"].character - 1))
vim.cmd("normal! \\<Esc>")
vim.api.nvim_win_set_cursor(0, { sl, sc })
vim.cmd("normal! v")
vim.api.nvim_win_set_cursor(0, { el, ec })
end
vim.keymap.set({ "n", "v" }, "+", function()
local sr = vim.lsp.buf_request_sync(buf, "textDocument/selectionRange", {
textDocument = vim.lsp.util.make_text_document_params(buf),
positions = { vim.lsp.util.make_position_params(0, "utf-16").position },
}, 1000)
if not sr then return end
for _, res in pairs(sr) do
if res.result and res.result[1] then
local ranges = flatten_sr(res.result[1])
local idx = #sel_stack + 1
if idx <= #ranges then
sel_stack[idx] = ranges[idx]
set_visual(ranges[idx])
end
return
end
end
end, opts)
vim.keymap.set("v", "-", function()
if #sel_stack > 1 then
table.remove(sel_stack)
set_visual(sel_stack[#sel_stack])
elseif #sel_stack == 1 then
sel_stack = {}
vim.cmd("normal! \\<Esc>")
end
end, opts)
vim.api.nvim_create_autocmd("ModeChanged", {
pattern = "v:n",
callback = function() sel_stack = {} end,
})
vim.keymap.set("i", "<C-s>", vim.lsp.buf.signature_help, opts)
vim.api.nvim_create_autocmd("TextChangedI", {
buffer = buf,
callback = function()
local col = vim.fn.col(".") - 1
if col <= 0 then return end
local line = vim.api.nvim_get_current_line()
local before = line:sub(1, col)
local char = before:sub(-1)
if char == "(" or char == "," then
vim.schedule(function()
if vim.fn.mode() == "i" then vim.lsp.buf.signature_help() end
end)
return
end
local opens = select(2, before:gsub("%(", ""))
local closes = select(2, before:gsub("%)", ""))
if opens > closes then
vim.schedule(function()
if vim.fn.mode() == "i" then vim.lsp.buf.signature_help() end
end)
end
end,
})
vim.keymap.set("n", "<leader>f", vim.lsp.buf.format, opts)
vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
vim.keymap.set("i", "<C-Space>", function()
vim.lsp.completion.get()
end, opts)
vim.api.nvim_create_autocmd("InsertCharPre", {
buffer = buf,
callback = function()
if vim.fn.pumvisible() == 1 then return end
local char = vim.v.char
if not char:match("[%w_]") then return end
local col = vim.fn.col(".") - 1 if col <= 0 then return end
local line = vim.api.nvim_get_current_line()
local before = line:sub(1, col)
local word = before:match("[%a_][%w_:]*$")
if not word then return end
vim.schedule(function()
if vim.fn.mode() == "i" and vim.fn.pumvisible() == 0 then
vim.lsp.completion.get()
end
end)
end,
})
print("perl-lsp attached! gd=def gr=refs K=hover <leader>rn=rename <leader>o=symbols <leader>f=format")
end,
})
vim.highlight.priorities.semantic_tokens = 200
vim.api.nvim_set_hl(0, "@lsp.type.variable.perl", { fg = "#61afef" }) vim.api.nvim_set_hl(0, "@lsp.type.parameter.perl", { fg = "#ff9e64", bold = true }) vim.api.nvim_set_hl(0, "@lsp.type.function.perl", { fg = "#7aa2f7" }) vim.api.nvim_set_hl(0, "@lsp.type.method.perl", { fg = "#7dcfff" }) vim.api.nvim_set_hl(0, "@lsp.type.macro.perl", { fg = "#bb9af7", bold = true }) vim.api.nvim_set_hl(0, "@lsp.type.property.perl", { fg = "#73daca" }) vim.api.nvim_set_hl(0, "@lsp.type.namespace.perl", { fg = "#e0af68", bold = true }) vim.api.nvim_set_hl(0, "@lsp.type.keyword.perl", { fg = "#ff007c", bold = true })
vim.api.nvim_set_hl(0, "@lsp.typemod.keyword.declaration.perl", { fg = "#ff007c", bold = true, underline = true })
vim.api.nvim_set_hl(0, "@lsp.type.enumMember.perl", { fg = "#ff9e64", italic = true }) vim.api.nvim_set_hl(0, "@lsp.type.regexp.perl", { fg = "#9ece6a" })
vim.api.nvim_set_hl(0, "@lsp.mod.declaration.perl", { bold = true, underline = true })
vim.api.nvim_set_hl(0, "@lsp.mod.modification.perl", { fg = "#f7768e" }) vim.api.nvim_set_hl(0, "@lsp.mod.readonly.perl", { italic = true })
vim.api.nvim_set_hl(0, "@lsp.mod.defaultLibrary.perl", { italic = true })
vim.api.nvim_set_hl(0, "@lsp.mod.deprecated.perl", { strikethrough = true })
vim.api.nvim_set_hl(0, "@lsp.mod.scalar.perl", { fg = "#61afef" }) vim.api.nvim_set_hl(0, "@lsp.mod.array.perl", { fg = "#c678dd" }) vim.api.nvim_set_hl(0, "@lsp.mod.hash.perl", { fg = "#e5c07b" })