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("!", "", "") vim.keymap.set("v", ">", ">gv") vim.keymap.set("v", "<", "", ":nohlsearch") ---- 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 = "", node_incremental = "", node_decremental = "", }, }, 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", "regex", "ron", "rst", "rust", "scss", "sql", "terraform", "toml", "typescript", "vim", "vimdoc", "xml", "yaml", }, highlight = { enable = true, indent = true }, }) ---- 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 = "", }) vim.keymap.set({ "i", "s" }, "", function() if luasnip.expand_or_jumpable() then luasnip.expand_or_jump() else vim.api.nvim_feedkeys( vim.api.nvim_replace_termcodes("", true, false, true), "n", -- noremap to avoid infinite recursion true ) end end, { silent = true }) vim.keymap.set({ "i", "s" }, "", 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({ [""] = cmp.mapping.scroll_docs(-4), [""] = cmp.mapping.scroll_docs(4), [""] = cmp.mapping.complete(), [""] = cmp.mapping.abort(), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items. [""] = 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", }, 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", "", vim.lsp.buf.signature_help) vim.keymap.set("n", "D", vim.lsp.buf.type_definition) vim.keymap.set("n", "rn", vim.lsp.buf.rename) vim.keymap.set("n", "ca", vim.lsp.buf.code_action) vim.keymap.set("n", "gr", vim.lsp.buf.references) vim.keymap.set("n", "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", "q", vim.diagnostic.setloclist) vim.keymap.set("n", "", 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 = {}, } 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() 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" }, "oc", gitblame.open_commit_url) vim.keymap.set({ "n" }, "tb", ":GitBlameToggle") ---- 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 "" 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 "" 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", "f", builtin.find_files) vim.keymap.set("n", "b", builtin.buffers) vim.keymap.set("n", "/", builtin.current_buffer_fuzzy_find) vim.keymap.set("n", "g", builtin.live_grep) vim.keymap.set("n", "cg", builtin.grep_string) vim.keymap.set("n", ":", builtin.commands) vim.keymap.set("n", "s", telescope.extensions.luasnip.luasnip) ---- nvim-dap vim.g.dap_loaded = false vim.api.nvim_create_autocmd({ "FileType" }, { pattern = { "go" }, 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", "dc", dap.continue) vim.keymap.set("n", "do", dap.step_over) vim.keymap.set("n", "di", dap.step_into) vim.keymap.set("n", "dO", dap.step_out) vim.keymap.set("n", "db", dap.toggle_breakpoint) vim.keymap.set("n", "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 -- nvim-dap-go require("dap-go").setup() end, }) ---- web-devicons require("nvim-web-devicons").setup() ---- nvim-osc52 vim.keymap.set("n", "y", require("osc52").copy_operator, { expr = true }) vim.keymap.set("n", "yy", "y_", { remap = true }) vim.keymap.set("v", "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", "cv", crates.show_versions_popup) vim.keymap.set("n", "cf", crates.show_features_popup) vim.keymap.set("n", "cd", crates.show_dependencies_popup) vim.keymap.set("n", "cu", crates.update_crate) vim.keymap.set("v", "cu", crates.update_crates) vim.keymap.set("n", "cA", crates.update_all_crates) vim.keymap.set("n", "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"), }, }) vim.keymap.set("n", "tr", function() neotest.run.run() end) vim.keymap.set("n", "tf", function() neotest.run.run(vim.fn.expand("%")) end) vim.keymap.set("n", "ts", function() neotest.run.stop() end) vim.keymap.set("n", "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", "lua require('spider').motion('w')", { desc = "Spider-w" }) vim.keymap.set({ "n", "o", "x" }, "e", "lua require('spider').motion('e')", { desc = "Spider-e" }) vim.keymap.set({ "n", "o", "x" }, "b", "lua require('spider').motion('b')", { desc = "Spider-b" }) vim.keymap.set({ "n", "o", "x" }, "ge", "lua require('spider').motion('ge')", { desc = "Spider-ge" }) --- colorizer require 'colorizer'.setup()