1
0
Fork 0
neovim-config/init.lua

790 lines
19 KiB
Lua

vim.loader.enable()
---- General Config ----
vim.opt.mouse = "a"
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.number = true
vim.opt.breakindent = true
vim.opt.showmatch = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.joinspaces = false
vim.opt.showmode = false
vim.opt.hidden = true
vim.opt.backup = false
vim.opt.writebackup = false
vim.opt.swapfile = false
vim.opt.shortmess:append({ I = true })
-- whichwrap -- left/right keys can traverse up/down
vim.opt.whichwrap:append("<,>,[,]")
vim.opt.cmdheight = 2
vim.opt.showcmd = true
vim.opt.splitright = true
vim.opt.splitbelow = true
vim.g.mapleader = " "
---- Terminal ----
vim.api.nvim_create_autocmd({ "TermOpen" }, {
callback = function()
vim.opt_local.number = false
end,
})
---- Theming ----
vim.opt.termguicolors = true
vim.opt.background = "dark"
vim.g.gruvbox_italic = true
vim.cmd.colorscheme("gruvbox")
vim.opt.listchars = {
eol = "",
tab = "▶▹",
nbsp = "",
extends = "",
trail = "",
}
---- lualine.nvim ----
local lualine = require("lualine")
local theme = require("lualine.themes.gruvbox")
theme.terminal = theme.insert
lualine.setup({
options = {
component_separators = { left = "", right = "" },
section_separators = { left = "", right = "" },
theme = theme,
},
sections = {
lualine_a = { "mode" },
lualine_b = { "diagnostics" },
lualine_c = {
{
"filename",
path = 1,
},
},
lualine_x = {
{
"fileformat",
cond = function()
return vim.bo.fileformat ~= "unix"
end,
},
{
"encoding",
cond = function()
return vim.bo.fileencoding ~= "utf-8"
end,
},
},
lualine_y = {
{
"filetype",
icons_enabled = false,
symbols = {
modified = "",
readonly = "",
unnamed = "",
new = "󰎔",
},
},
},
lualine_z = { "searchcount", "progress", "location" },
},
tabline = {
lualine_a = {
{
"buffers",
show_filename_only = false,
max_length = function()
return math.max(math.ceil(vim.o.columns * 2 / 3), vim.o.columns - 6)
end,
},
},
lualine_z = { "tabs" },
},
})
---- Maps ----
vim.keymap.set("!", "<C-BS>", "<C-w>")
vim.keymap.set("v", ">", ">gv")
vim.keymap.set("v", "<", "<gv")
vim.keymap.set("n", "<C-L>", ":nohlsearch<CR>")
---- Treesitter ----
require("nvim-treesitter.configs").setup({
auto_install = true,
sync_install = false,
ignore_install = {},
modules = {}, -- not used, but it will silence lua_ls
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<Leader><Enter>",
node_incremental = "<Enter>",
node_decremental = "<BS>",
},
},
ensure_installed = {
"bash",
"c",
"cmake",
"comment",
"cpp",
"css",
"csv",
"diff",
"dockerfile",
"dot",
"git_config",
"git_rebase",
"gitattributes",
"gitcommit",
"gitignore",
"go",
"html",
"htmldjango",
"http",
"ini",
"javascript",
"json",
"kdl",
"kotlin",
"latex",
"lua",
"make",
"markdown",
"markdown_inline",
"mermaid",
"passwd",
"perl",
"php",
"proto",
"python",
"query",
"regex",
"ron",
"rst",
"rust",
"scss",
"sql",
"terraform",
"toml",
"typescript",
"vim",
"vimdoc",
"xml",
"yaml",
},
highlight = { enable = true, indent = true },
playground = {
enable = true,
disable = {},
updatetime = 25, -- Debounced time for highlighting nodes in the playground from source code
persist_queries = false, -- Whether the query persists across vim sessions
keybindings = {
toggle_query_editor = 'o',
toggle_hl_groups = 'i',
toggle_injected_languages = 't',
toggle_anonymous_nodes = 'a',
toggle_language_display = 'I',
focus_language = 'f',
unfocus_language = 'F',
update = 'R',
goto_node = '<cr>',
show_help = '?',
},
},
query_linter = {
enable = true,
use_virtual_text = true,
lint_events = { "BufWrite", "CursorHold" },
},
})
---- Filetypes ---
-- KDL --
vim.filetype.add({ extension = { kdl = "kdl" } })
-- Markdown --
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = "markdown",
callback = function()
vim.opt_local.textwidth = 85
vim.opt_local.spell = true
end,
})
-- YAML --
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "yaml", "helm", },
callback = function()
vim.opt_local.softtabstop = 2
vim.opt_local.expandtab = true
vim.opt_local.foldmethod = "indent"
end,
})
---- formatter.nvim ----
require("formatter").setup({
filetype = {
terraform = {
require("formatter.filetypes.terraform").terraformfmt,
},
},
["*"] = {
-- "formatter.filetypes.any" defines default configurations for any
-- filetype
require("formatter.filetypes.any").remove_trailing_whitespace,
},
})
---- LuaSnip
local luasnip = require("luasnip")
require("luasnip.loaders.from_lua").lazy_load()
luasnip.config.set_config({
store_selection_keys = "<c-s>",
})
vim.keymap.set({ "i", "s" }, "<Tab>", function()
if luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
vim.api.nvim_feedkeys(
vim.api.nvim_replace_termcodes("<Tab>", true, false, true),
"n", -- noremap to avoid infinite recursion
true
)
end
end, { silent = true })
vim.keymap.set({ "i", "s" }, "<S-Tab>", function()
if luasnip.jumpable(-1) then
luasnip.jump(-1)
end
end, { silent = true })
---- completion-nvim
local cmp = require("cmp")
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
window = {
-- completion = cmp.config.window.bordered(),
-- documentation = cmp.config.window.bordered(),
},
mapping = cmp.mapping.preset.insert({
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping.abort(),
-- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
["<CR>"] = cmp.mapping.confirm({ select = false }),
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "path" },
{ name = "buffer" },
{ name = "crates" },
}),
})
---- LSP ----
vim.g.lsp_loaded = false
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = {
"c",
"cpp",
"dot",
"go",
"javascript",
"json",
"latex",
"lua",
"python",
"rust",
"terraform",
"tex",
"toml",
"typescript",
"typst",
},
callback = function()
if vim.g.lsp_loaded == true then
return
end
vim.g.lsp_loaded = true
vim.cmd("packadd nvim-lspconfig")
vim.cmd("packadd lsp_lines.nvim")
-- vim.cmd("packadd lsp-timeout.nvim")
local lsp = require("lspconfig")
-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local on_lsp_attach = function(_client, _bufnr)
vim.keymap.set("n", "gD", vim.lsp.buf.declaration)
vim.keymap.set("n", "gd", vim.lsp.buf.definition)
vim.keymap.set("n", "K", vim.lsp.buf.hover)
vim.keymap.set("n", "gi", vim.lsp.buf.implementation)
vim.keymap.set("n", "<C-k>", vim.lsp.buf.signature_help)
vim.keymap.set("n", "<Leader>D", vim.lsp.buf.type_definition)
vim.keymap.set("n", "<Leader>rn", vim.lsp.buf.rename)
vim.keymap.set("n", "<Leader>ca", vim.lsp.buf.code_action)
vim.keymap.set("n", "gr", vim.lsp.buf.references)
vim.keymap.set("n", "<Leader>e", vim.diagnostic.open_float)
vim.keymap.set("n", "[d", vim.diagnostic.goto_prev)
vim.keymap.set("n", "]d", vim.diagnostic.goto_next)
vim.keymap.set("n", "<Leader>q", vim.diagnostic.setloclist)
vim.keymap.set("n", "<F3>", function()
vim.lsp.buf.format({ async = true })
end)
end
-- Use a loop to conveniently call 'setup' on multiple servers and
-- map buffer local keybindings when the language server attaches
local servers = {
clangd = {
filetypes = { "c", "cpp" }
},
dotls = {},
eslint = {},
gopls = {},
jsonls = {},
kotlin_language_server = {},
lua_ls = {
settings = {
Lua = {
runtime = { version = "LuaJIT" },
diagnostics = {
globals = { "vim" },
unusedLocalExclude = { "_*" },
},
workspace = {
checkThirdParty = false,
library = vim.api.nvim_get_runtime_file("", true),
},
telemetry = { enable = false },
},
},
},
pylsp = {},
rust_analyzer = {
settings = {
['rust-analyzer'] = {
cargo = {
features = "all"
}
}
}
},
taplo = {},
texlab = {},
tflint = {},
tsserver = {},
biome = {
cmd = { "pnpm", "exec", "biome", "lsp-proxy" }
},
typst_lsp = {},
}
local capabilities = require("cmp_nvim_lsp").default_capabilities()
lsp.util.default_config = vim.tbl_deep_extend("force", lsp.util.default_config, { capabilities = capabilities })
for server, add_to_config in pairs(servers) do
local config = {
on_attach = on_lsp_attach,
flags = {
debounce_text_changes = 150,
},
}
for k, v in pairs(add_to_config) do
config[k] = v
end
lsp[server].setup(config)
end
vim.cmd("LspStart")
---- lsp_lines
require("lsp_lines").setup()
-- Disable virtual_text since it's redundant due to lsp_lines.
vim.diagnostic.config({
virtual_text = false,
})
---- lsp-timeout
-- vim.g["lsp-timeout-config"] = {
-- stopTimeout = 1000 * 60 * 5, -- ms, timeout before stopping all LSP servers
-- startTimeout = 1000 * 2, -- ms, timeout before restart
-- silent = false -- true to suppress notifications
-- }
end,
})
-- Auto format on save
vim.api.nvim_create_autocmd({ "BufWritePre" }, {
callback = function()
if vim.b.disable_format then
return
end
local lsp_format = false
for _, client in pairs(vim.lsp.buf_get_clients()) do
if client.server_capabilities.documentFormattingProvider then
lsp_format = true
break
end
end
if lsp_format then
vim.lsp.buf.format({ async = false })
else
vim.cmd("Format")
end
end,
})
---- nvim-lint ----
local lint = require("lint")
local clippy_format = "%E%f:%l:%c: %\\d%#:%\\d%# %.%\\{-}"
.. "error:%.%\\{-} %m,%W%f:%l:%c: %\\d%#:%\\d%# %.%\\{-}"
.. "warning:%.%\\{-} %m,%C%f:%l %m,%-G,%-G"
.. "error: aborting %.%#,%-G"
.. "error: Could not compile %.%#,%E"
.. "error: %m,%Eerror[E%n]: %m,%-G"
.. "warning: the option `Z` is unstable %.%#,%W"
.. "warning: %m,%Inote: %m,%C %#--> %f:%l:%c"
-- clippy --
lint.linters.clippy = {
name = "clippy",
cmd = "cargo",
stdin = false,
append_fname = false,
args = { "clippy" },
stream = "stdout",
ignore_exitcode = true,
env = nil,
parser = require("lint.parser").from_errorformat(clippy_format),
}
-- sqlfluff --
lint.linters.sqlfluff.args = {
"lint",
"--format=json",
"--dialect=postgres",
}
-- yamllint --
lint.linters.yamllint.args = {
args = { "--format=parsable", "-d relaxed" },
}
local linters = {
bash = { "shellcheck" },
dockerfile = { "hadolint" },
go = { "golangcilint" },
python = { "flake8" },
-- rust = { "clippy" },
sql = { "sqlfluff" },
yaml = { "yamllint" },
}
-- set linters --
lint.linters_by_ft = linters
-- set autocommands --
for ft, ft_linters in pairs(linters) do
local on_change = false
local on_write = false
for _, l in ipairs(ft_linters) do
if lint.linters[l].stdin then
on_change = true
else
on_write = true
end
end
if on_change then
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = ft,
callback = function()
vim.api.nvim_create_autocmd({ "TextChanged" }, {
callback = function()
lint.try_lint(nil, { ignore_errors = true })
end,
})
end,
})
end
if on_write then
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = ft,
callback = function()
vim.api.nvim_create_autocmd({ "BufWritePost" }, {
callback = function()
lint.try_lint(nil, { ignore_errors = true })
end,
})
-- we want to also call linters here so we don't wait
-- for the first write to happen
lint.try_lint(nil, { ignore_errors = true })
end,
})
end
end
---- gitblame
local gitblame = require("gitblame")
gitblame.setup({
enabled = false,
})
vim.keymap.set({ "n" }, "<leader>oc", gitblame.open_commit_url)
vim.keymap.set({ "n" }, "<leader>tb", ":GitBlameToggle<CR>")
---- gitsigns.nvim
require("gitsigns").setup({
on_attach = function(_bufnr)
local gs = package.loaded.gitsigns
-- Navigation
vim.keymap.set("n", "]c", function()
if vim.wo.diff then
return "]c"
end
vim.schedule(function()
gs.next_hunk()
end)
return "<Ignore>"
end, { expr = true })
vim.keymap.set("n", "[c", function()
if vim.wo.diff then
return "[c"
end
vim.schedule(function()
gs.prev_hunk()
end)
return "<Ignore>"
end, { expr = true })
end,
})
---- Comment.nvim
require("Comment").setup()
---- Telescope
local telescope = require("telescope")
telescope.setup({
defaults = {
file_ignore_patterns = { "%.avif", "%.jpg", "%.pdf", "%.png", "%.webp" },
},
pickers = {
find_files = {
find_command = { "fd", "--type", "f", "--hidden", "--exclude", ".git", "--exclude", ".gitmodules" },
},
},
})
telescope.load_extension("fzy_native")
telescope.load_extension("luasnip")
local builtin = require("telescope.builtin")
vim.keymap.set("n", "<Leader>f", builtin.find_files)
vim.keymap.set("n", "<Leader>b", builtin.buffers)
vim.keymap.set("n", "<Leader>/", builtin.current_buffer_fuzzy_find)
vim.keymap.set("n", "<Leader>g", builtin.live_grep)
vim.keymap.set("n", "<Leader>cg", builtin.grep_string)
vim.keymap.set("n", "<Leader>:", builtin.commands)
vim.keymap.set("n", "<Leader>s", telescope.extensions.luasnip.luasnip)
---- nvim-dap
vim.g.dap_loaded = false
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "go", "rust" },
callback = function()
vim.g.dap_loaded = true
vim.cmd("packadd nvim-dap")
vim.cmd("packadd nvim-dap-ui")
vim.cmd("packadd nvim-dap-go")
local dap = require("dap")
vim.fn.sign_define("DapBreakpoint", { text = "🟥", texthl = "", linehl = "", numhl = "" })
vim.fn.sign_define("DapStopped", { text = "▶️", texthl = "", linehl = "", numhl = "" })
-- Set keymaps to control the debugger
vim.keymap.set("n", "<leader>dc", dap.continue)
vim.keymap.set("n", "<leader>do", dap.step_over)
vim.keymap.set("n", "<leader>di", dap.step_into)
vim.keymap.set("n", "<leader>dO", dap.step_out)
vim.keymap.set("n", "<leader>db", dap.toggle_breakpoint)
vim.keymap.set("n", "<leader>dB", function()
dap.set_breakpoint(vim.fn.input("Breakpoint condition: "))
end)
local dapui = require("dapui")
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open()
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close()
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close()
end
dapui.setup()
-- rust
dap.adapters.lldb = {
type = 'executable',
command = '/usr/bin/lldb-vscode',
name = 'lldb'
}
dap.configurations.rust = {
{
name = 'Launch',
type = 'lldb',
request = 'launch',
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
cwd = '${workspaceFolder}',
stopOnEntry = false,
args = {},
initCommands = function()
-- Find out where to look for the pretty printer Python module
local rustc_sysroot = vim.fn.trim(vim.fn.system('rustc --print sysroot'))
local script_import = 'command script import "' .. rustc_sysroot .. '/lib/rustlib/etc/lldb_lookup.py"'
local commands_file = rustc_sysroot .. '/lib/rustlib/etc/lldb_commands'
local commands = {}
local file = io.open(commands_file, 'r')
if file then
for line in file:lines() do
table.insert(commands, line)
end
file:close()
end
table.insert(commands, 1, script_import)
return commands
end,
},
}
-- nvim-dap-go
require("dap-go").setup()
end,
})
---- web-devicons
require("nvim-web-devicons").setup()
---- nvim-osc52
vim.keymap.set("n", "<leader>y", require("osc52").copy_operator, { expr = true })
vim.keymap.set("n", "<leader>yy", "<leader>y_", { remap = true })
vim.keymap.set("v", "<leader>y", require("osc52").copy_visual)
---- crates
vim.g.crates_loaded = false
vim.api.nvim_create_autocmd({ "BufEnter" }, {
pattern = { "Cargo.toml" },
callback = function()
if vim.g.crates_loaded then
return
end
vim.g.crates_loaded = true
vim.cmd("packadd crates.nvim")
local crates = require("crates")
crates.setup()
vim.keymap.set("n", "<leader>cv", crates.show_versions_popup)
vim.keymap.set("n", "<leader>cf", crates.show_features_popup)
vim.keymap.set("n", "<leader>cd", crates.show_dependencies_popup)
vim.keymap.set("n", "<leader>cu", crates.update_crate)
vim.keymap.set("v", "<leader>cu", crates.update_crates)
vim.keymap.set("n", "<leader>cA", crates.update_all_crates)
vim.keymap.set("n", "<leader>cD", crates.open_documentation)
end,
})
---- neotest
vim.g.neotest_loaded = false
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "rust" },
callback = function()
if vim.g.neotest_loaded then
return
end
vim.g.neotest_loaded = true
vim.cmd("packadd neotest")
vim.cmd("packadd neotest-rust")
local neotest = require("neotest")
neotest.setup({
adapters = {
require("neotest-rust") {
args = { "--no-capture" },
dap_adapter = "lldb",
},
},
})
vim.keymap.set("n", "<leader>tr", function()
neotest.run.run()
end)
vim.keymap.set("n", "<leader>td", function()
neotest.run.run({ strategy = "dap" })
end)
vim.keymap.set("n", "<leader>tf", function()
neotest.run.run(vim.fn.expand("%"))
end)
vim.keymap.set("n", "<leader>ts", function()
neotest.run.stop()
end)
vim.keymap.set("n", "<leader>tt", function()
neotest.summary.toggle()
end)
end,
})
---- nvim-surround
require("nvim-surround").setup()
---- nvim-spider
-- we need to use the ex-command version to cal the commands to allow dot repeatability
-- see: https://github.com/chrisgrieser/nvim-spider#installation
vim.keymap.set({ "n", "o", "x" }, "w", "<cmd>lua require('spider').motion('w')<CR>", { desc = "Spider-w" })
vim.keymap.set({ "n", "o", "x" }, "e", "<cmd>lua require('spider').motion('e')<CR>", { desc = "Spider-e" })
vim.keymap.set({ "n", "o", "x" }, "b", "<cmd>lua require('spider').motion('b')<CR>", { desc = "Spider-b" })
vim.keymap.set({ "n", "o", "x" }, "ge", "<cmd>lua require('spider').motion('ge')<CR>", { desc = "Spider-ge" })
---- colorizer
require 'colorizer'.setup({
user_default_options = {
names = false,
css_fn = true,
}
})
---- ferris.nvim
vim.g.ferris_loaded = false
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "rust" },
callback = function()
if vim.g.ferris_loaded then
return
end
vim.g.ferris_loaded = true
vim.cmd("packadd ferris.nvim")
require("ferris").setup()
vim.keymap.set("n", "<leader>od", require("ferris.methods.open_documentation"))
end,
})
---- typst-preview
vim.g.typst_loaded = false
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "typst" },
callback = function()
if vim.g.typst_loaded then
return
end
vim.g.typst_loaded = true
vim.cmd("packadd core.nvim")
vim.cmd("packadd typst-preview.nvim")
local typst_preview = require("typst-preview")
typst_preview.setup({
preview = function(output_file)
require("core").job.spawn("xdg-open", {
output_file,
}, {}, function() end, function() end, function() end)
end,
})
typst_preview.preview()
end,
})