1
0
Fork 0
dotfiles/.config/nvim/lua/funcs.lua

446 lines
16 KiB
Lua
Raw Normal View History

local M = {}
2024-01-25 00:10:11 +01:00
local icons = require("config.icons")
function M.set_title()
local title = " %t"
local f = io.popen([[zsh -c '
source $XDG_CONFIG_HOME/zsh/configs/autogenerated/hashes
print -Pn "$USER@$HOST [%3~] "
']])
if f ~= nil then
title = f:read("*a") or ""
title = title .. " %t"
f:close()
end
vim.opt.titlestring = title
end
2024-01-24 00:48:37 +01:00
function M.buf_close(bufnr, force)
2024-01-25 01:25:00 +01:00
local kill_command = "bd"
local bo = vim.bo
local api = vim.api
local fnamemodify = vim.fn.fnamemodify
if bufnr == 0 or bufnr == nil then
bufnr = api.nvim_get_current_buf()
end
local bufname = api.nvim_buf_get_name(bufnr)
if not force then
local warning
if bo[bufnr].modified then
warning = string.format([[No write since last change for (%s)]], fnamemodify(bufname, ":t"))
elseif api.nvim_buf_get_option(bufnr, "buftype") == "terminal" then
warning = string.format([[Terminal %s will be killed]], bufname)
end
if warning then
vim.ui.input({
prompt = string.format([[%s. Close it anyway? [y]es or [n]o (default: no): ]], warning),
}, function(choice)
if choice:match "ye?s?" then force = true end
end)
if not force then return end
end
end
-- Get list of window IDs with the buffer to close
local windows = vim.tbl_filter(function(win)
return api.nvim_win_get_buf(win) == bufnr
end, api.nvim_list_wins())
if #windows == 0 then return end
if force then
kill_command = kill_command .. "!"
end
-- Get list of active buffers
local buffers = vim.tbl_filter(function(buf)
return api.nvim_buf_is_valid(buf) and bo[buf].buflisted
end, api.nvim_list_bufs())
-- If there is only one buffer (which has to be the current one), vim will
-- create a new buffer on :bd.
-- For more than one buffer, pick the previous buffer (wrapping around if necessary)
if #buffers > 1 then
for i, v in ipairs(buffers) do
if v == bufnr then
local prev_buf_idx = i == 1 and (#buffers - 1) or (i - 1)
local prev_buffer = buffers[prev_buf_idx]
for _, win in ipairs(windows) do
api.nvim_win_set_buf(win, prev_buffer)
end
end
end
else
vim.cmd('q!')
end
-- Check if buffer still exists, to ensure the target buffer wasn't killed
-- due to options like bufhidden=wipe.
if api.nvim_buf_is_valid(bufnr) and bo[bufnr].buflisted then
vim.cmd(string.format("%s %d", kill_command, bufnr))
end
2024-01-24 00:48:37 +01:00
end
2024-01-24 00:48:37 +01:00
function M.is_available(plugin)
local lazy_config_avail, lazy_config = pcall(require, "lazy.core.config")
return lazy_config_avail and lazy_config.spec.plugins[plugin] ~= nil
end
2024-01-24 00:48:37 +01:00
function M.empty_map_table()
local maps = {}
for _, mode in ipairs { "", "n", "v", "x", "s", "o", "!", "i", "l", "c", "t" } do
maps[mode] = {}
end
2024-01-25 00:10:11 +01:00
-- TODO: Check this on 0.10.0 release
2024-01-24 00:48:37 +01:00
if vim.fn.has "nvim-0.10.0" == 1 then
for _, abbr_mode in ipairs { "ia", "ca", "!a" } do
maps[abbr_mode] = {}
end
end
2024-01-24 00:48:37 +01:00
return maps
end
2024-01-24 00:48:37 +01:00
function M.toggle_term_cmd(opts)
if not vim.g.user_terminals then
vim.g.user_terminals = {}
end
2024-01-24 00:48:37 +01:00
local terms = vim.g.user_terminals
if type(opts) == "string" then opts = { cmd = opts, hidden = true } end
local num = vim.v.count > 0 and vim.v.count or 1
if not terms[opts.cmd] then terms[opts.cmd] = {} end
if not terms[opts.cmd][num] then
if not opts.count then opts.count = vim.tbl_count(terms) * 100 + num end
if not opts.on_exit then opts.on_exit = function() terms[opts.cmd][num] = nil end end
terms[opts.cmd][num] = require("toggleterm.terminal").Terminal:new(opts)
end
terms[opts.cmd][num]:toggle()
end
2024-01-24 00:48:37 +01:00
-- TODO: test this
function M.file_worktree(file, worktrees)
worktrees = worktrees or vim.g.git_worktrees
if not worktrees then return end
file = file or vim.fn.expand("%")
for _, worktree in ipairs(worktrees) do
local r = vim.fn.system({
"git",
"--work-tree",
worktree.toplevel,
"--git-dir",
worktree.gitdir,
"ls-files",
"--error-unmatch",
file
2024-01-27 19:55:34 +01:00
---@diagnostic disable-next-line: undefined-field
2024-01-24 00:48:37 +01:00
}):wait()
if r.code == 0 then
return worktree
end
end
2024-01-24 00:48:37 +01:00
end
2024-01-24 00:48:37 +01:00
function M.which_key_register()
2024-01-25 00:10:11 +01:00
if M.which_key_queue then
local wk_avail, wk = pcall(require, "which-key")
if wk_avail then
for mode, registration in pairs(M.which_key_queue) do
wk.register(registration, { mode = mode })
end
M.which_key_queue = nil
end
end
2024-01-24 00:48:37 +01:00
end
function M.set_mappings(map_table, base)
2024-01-25 00:10:11 +01:00
base = base or {}
for mode, maps in pairs(map_table) do
for keymap, options in pairs(maps) do
if options then
local cmd = options
local keymap_opts = base
if type(options) == "table" then
cmd = options[1]
keymap_opts = vim.tbl_deep_extend("force", keymap_opts, options)
keymap_opts[1] = nil
end
if not cmd or keymap_opts.name then -- which-key mapping
if not keymap_opts.name then keymap_opts.name = keymap_opts.desc end
if not M.which_key_queue then M.which_key_queue = {} end
if not M.which_key_queue[mode] then M.which_key_queue[mode] = {} end
M.which_key_queue[mode][keymap] = keymap_opts
else -- not which-key mapping
vim.keymap.set(mode, keymap, cmd, keymap_opts)
end
end
2024-01-24 00:48:37 +01:00
end
2024-01-25 00:10:11 +01:00
end
if package.loaded["which-key"] then M.which_key_register() end
end
local function del_buffer_autocmd(augroup, bufnr)
local cmds_found, cmds = pcall(vim.api.nvim_get_autocmds, { group = augroup, buffer = bufnr })
if cmds_found then vim.tbl_map(function(cmd) vim.api.nvim_del_autocmd(cmd.id) end, cmds) end
end
local function add_buffer_autocmd(augroup, bufnr, autocmds)
if not vim.tbl_islist(autocmds) then autocmds = { autocmds } end
local cmds_found, cmds = pcall(vim.api.nvim_get_autocmds, { group = augroup, buffer = bufnr })
if not cmds_found or vim.tbl_isempty(cmds) then
vim.api.nvim_create_augroup(augroup, { clear = false })
for _, autocmd in ipairs(autocmds) do
local events = autocmd.events
autocmd.events = nil
autocmd.group = augroup
autocmd.buffer = bufnr
vim.api.nvim_create_autocmd(events, autocmd)
2024-01-24 00:48:37 +01:00
end
end
2024-01-25 00:10:11 +01:00
end
local function has_capability(capability, filter)
for _, client in ipairs(vim.lsp.get_active_clients(filter)) do
if client.supports_method(capability) then return true end
end
return false
end
2024-01-25 01:06:53 +01:00
-- NOTE: LSP Keymaps here
2024-01-24 10:53:48 +01:00
function M.lsp_on_attach(client, bufnr)
local lsp_mappings = M.empty_map_table()
lsp_mappings.n["<leader>ld"] = { function() vim.diagnostic.open_float() end, desc = "Hover diagnostics" }
lsp_mappings.n["[d"] = { function() vim.diagnostic.goto_prev() end, desc = "Previous diagnostic" }
lsp_mappings.n["]d"] = { function() vim.diagnostic.goto_next() end, desc = "Next diagnostic" }
lsp_mappings.n["gl"] = { function() vim.diagnostic.open_float() end, desc = "Hover diagnostics" }
if M.is_available("telescope.nvim") then
lsp_mappings.n["<leader>lD"] = {
function() require("telescope.builtin").diagnostics() end,
desc = "Search diagnostics",
}
end
2024-01-25 00:10:11 +01:00
if M.is_available("mason-lspconfig.nvim") then
2024-01-24 10:53:48 +01:00
lsp_mappings.n["<leader>li"] = { "<cmd>LspInfo<cr>", desc = "LSP information" }
end
2024-01-27 19:55:34 +01:00
if M.is_available("none-ls.nvim") then
2024-01-24 10:53:48 +01:00
lsp_mappings.n["<leader>lI"] = { "<cmd>NullLsInfo<cr>", desc = "Null-ls information" }
end
2024-01-25 00:10:11 +01:00
if client.supports_method("textDocument/codeAction") then
2024-01-24 10:53:48 +01:00
lsp_mappings.n["<leader>la"] = {
function() vim.lsp.buf.code_action() end,
2024-01-25 00:10:11 +01:00
desc = "Code action",
2024-01-24 10:53:48 +01:00
}
lsp_mappings.v["<leader>la"] = lsp_mappings.n["<leader>la"]
end
2024-01-25 00:10:11 +01:00
if client.supports_method("textDocument/codeLens") then
vim.lsp.codelens.refresh()
lsp_mappings.n["<leader>ll"] = {
function() vim.lsp.codelens.refresh() end,
desc = "Refresh CodeLens",
}
lsp_mappings.n["<leader>lL"] = {
function() vim.lsp.codelens.run() end,
desc = "Run CodeLens",
}
2024-01-26 17:21:37 +01:00
lsp_mappings.n["<Leader>u"] = { desc = " " .. icons.Gear .. " Utility" }
2024-01-25 01:06:53 +01:00
lsp_mappings.n["<leader>uL"] = {
2024-01-25 00:10:11 +01:00
function() vim.lsp.codelens.clear() end,
desc = "Toggle CodeLens"
}
end
if client.supports_method("textDocument/definition") then
lsp_mappings.n["gd"] = {
function() vim.lsp.buf.definition() end,
desc = "Go to definition",
}
end
if client.supports_method "textDocument/typeDefinition" then
lsp_mappings.n["gy"] = {
function() vim.lsp.buf.type_definition() end,
desc = "Go to type definition",
}
end
if client.supports_method("textDocument/declaration") then
lsp_mappings.n["gD"] = {
function() vim.lsp.buf.declaration() end,
desc = "Go to declaration",
}
end
if client.supports_method("textDocument/implementation") then
lsp_mappings.n["gI"] = {
function() vim.lsp.buf.implementation() end,
desc = "List implementations",
}
end
if client.supports_method("textDocument/references") then
lsp_mappings.n["gr"] = {
function() vim.lsp.buf.references() end,
desc = "List references",
}
end
if client.supports_method "workspace/symbol" then
lsp_mappings.n["<leader>lG"] = {
function() vim.lsp.buf.workspace_symbol() end,
desc = "List symbols"
}
end
if client.supports_method "textDocument/rename" then
lsp_mappings.n["<leader>lr"] = {
function() vim.lsp.buf.rename() end,
desc = "Rename symbol",
}
end
-- TODO: Check this on 0.10.0 release
if client.supports_method "textDocument/semanticTokens/full" and vim.lsp.semantic_tokens then
vim.b[bufnr].semantic_tokens_enabled = true
2024-01-26 17:21:37 +01:00
lsp_mappings.n["<Leader>u"] = { desc = " " .. icons.Gear .. " Utility" }
2024-01-25 01:06:53 +01:00
lsp_mappings.n["<leader>uY"] = {
2024-01-25 00:10:11 +01:00
function()
vim.b[bufnr].semantic_tokens_enabled = not vim.b[bufnr].semantic_tokens_enabled
for _, active_client in ipairs(vim.lsp.get_active_clients { bufnr = bufnr }) do
if active_client.server_capabilities.semanticTokensProvider then
vim.lsp.semantic_tokens[vim.b[bufnr].semantic_tokens_enabled and "start" or "stop"](bufnr,
active_client.id)
end
end
end,
desc = "Toggle LSP semantic highlight (buffer)",
}
end
if client.supports_method("textDocument/formatting") then
lsp_mappings.n["<leader>lf"] = {
function()
2024-01-27 19:55:34 +01:00
vim.lsp.buf.format({
filter = function(c)
local filetype = vim.bo.filetype
local n = require "null-ls"
local s = require "null-ls.sources"
local method = n.methods.FORMATTING
local available_formatters = s.get_available(filetype, method)
if #available_formatters > 0 then
return c.name == "null-ls"
end
return true
end
})
2024-01-25 00:10:11 +01:00
end,
desc = "Format buffer",
}
lsp_mappings.v["<leader>lf"] = lsp_mappings.n["<leader>lf"]
end
if client.supports_method("textDocument/documentHighlight") then
add_buffer_autocmd("lsp_document_highlight", bufnr, {
{
events = { "CursorHold", "CursorHoldI" },
desc = "highlight references when cursor holds",
callback = function()
if not has_capability("textDocument/documentHighlight", { bufnr = bufnr }) then
del_buffer_autocmd("lsp_document_highlight", bufnr)
return
end
vim.lsp.buf.document_highlight()
end,
},
{
events = { "CursorMoved", "CursorMovedI", "BufLeave" },
desc = "clear references when cursor moves",
callback = function() vim.lsp.buf.clear_references() end,
},
})
end
if client.supports_method("textDocument/hover") then
-- TODO: Check this on 0.10.0 release
if vim.fn.has "nvim-0.10" == 0 then
lsp_mappings.n["K"] = {
function() vim.lsp.buf.hover() end,
desc = "Hover symbol",
}
end
end
if client.supports_method("textDocument/inlayHint") then
if vim.b.inlay_hints_enabled == nil then vim.b.inlay_hints_enabled = true end
-- TODO: Check this on 0.10.0 release
if vim.lsp.inlay_hint then
if vim.b.inlay_hints_enabled then vim.lsp.inlay_hint.enable(bufnr, true) end
2024-01-26 17:21:37 +01:00
lsp_mappings.n["<Leader>u"] = { desc = " " .. icons.Gear .. " Utility" }
2024-01-25 01:06:53 +01:00
lsp_mappings.n["<leader>uH"] = {
2024-01-25 00:10:11 +01:00
function()
vim.b[bufnr].inlay_hints_enabled = not vim.b[bufnr].inlay_hints_enabled
if vim.lsp.inlay_hint then
vim.lsp.inlay_hint.enable(bufnr, vim.b[bufnr].inlay_hints_enabled)
end
end,
desc = "Toggle inlay hints",
}
end
end
if client.supports_method "textDocument/signatureHelp" then
lsp_mappings.n["<leader>lh"] = {
function() vim.lsp.buf.signature_help() end,
desc = "Signature help",
}
end
if M.is_available "telescope.nvim" then
if lsp_mappings.n.gd then
lsp_mappings.n.gd[1] = function() require("telescope.builtin").lsp_definitions() end
end
if lsp_mappings.n.gI then
lsp_mappings.n.gI[1] = function() require("telescope.builtin").lsp_implementations() end
end
if lsp_mappings.n.gr then
lsp_mappings.n.gr[1] = function() require("telescope.builtin").lsp_references() end
end
if lsp_mappings.n["<leader>lR"] then
lsp_mappings.n["<leader>lR"][1] = function() require("telescope.builtin").lsp_references() end
end
if lsp_mappings.n.gy then
lsp_mappings.n.gy[1] = function() require("telescope.builtin").lsp_type_definitions() end
end
if lsp_mappings.n["<leader>lG"] then
lsp_mappings.n["<leader>lG"][1] = function()
vim.ui.input({ prompt = "Symbol Query: (leave empty for word under cursor)" }, function(query)
if query then
-- word under cursor if given query is empty
if query == "" then query = vim.fn.expand "<cword>" end
require("telescope.builtin").lsp_workspace_symbols {
query = query,
prompt_title = ("Find word (%s)"):format(query),
}
end
end)
end
end
end
2024-01-24 10:53:48 +01:00
2024-01-25 00:10:11 +01:00
if not vim.tbl_isempty(lsp_mappings.v) then
2024-01-26 17:21:37 +01:00
if lsp_mappings.v["<leader>l"] then lsp_mappings.v["<leader>l"] = { desc = " " .. icons.Code .. " LSP" } end
if lsp_mappings.v["<leader>u"] then lsp_mappings.v["<leader>u"] = { desc = " " .. icons.Code .. " LSP" } end
2024-01-25 00:10:11 +01:00
end
2024-01-24 10:53:48 +01:00
2024-01-25 00:10:11 +01:00
M.set_mappings(lsp_mappings, { buffer = bufnr })
end
2024-01-24 10:53:48 +01:00
return M