vim.loader.enable() local g = vim.g local opt = vim.opt local autocmd = vim.api.nvim_create_autocmd local augroup = vim.api.nvim_create_augroup local hi = vim.api.nvim_set_hl local map = vim.keymap.set local nmap = function(lhs, rhs, opts) vim.keymap.set("n", lhs, rhs, opts) end local vmap = function(lhs, rhs, opts) vim.keymap.set("v", lhs, rhs, opts) end -- PackChanged hook for nvim-treesitter to run TSUpdate after update vim.api.nvim_create_autocmd("PackChanged", { callback = function(ev) local name, kind = ev.data.spec.name, ev.data.kind if name == "nvim-treesitter" and kind == "update" then if not ev.data.active then vim.cmd.packadd("nvim-treesitter") end vim.cmd("TSUpdate") end end, }) -- Plugins local github = function(repo) return "https://github.com/" .. repo end vim.pack.add(vim.tbl_map(github, { "airblade/vim-gitgutter", "catgoose/nvim-colorizer.lua", "folke/trouble.nvim", "github/copilot.vim", "hrsh7th/vim-vsnip", "jamessan/vim-gnupg", "nvim-lua/plenary.nvim", "nvim-telescope/telescope.nvim", "nvim-tree/nvim-tree.lua", "nvim-treesitter/nvim-treesitter", "saghen/blink.lib", "saghen/blink.cmp", "schickling/vim-bufonly", "stevearc/conform.nvim", "taigrr/blast.nvim", "tpope/vim-fugitive", "tpope/vim-surround", "tpope/vim-unimpaired", "vim-airline/vim-airline", })) g.mapleader = " " vim.cmd("colorscheme charmtone") local options = { compatible = false, backup = false, writebackup = false, swapfile = false, shell = "bash", number = true, wrap = false, expandtab = true, shiftwidth = 4, tabstop = 4, softtabstop = 4, termguicolors = true, showmatch = true, autoread = true, colorcolumn = "80", mouse = "a", spell = false, spelllang = { "en_us" }, cursorline = true, ruler = true, signcolumn = "yes", listchars = "tab:▸ ,trail:·,eol:¶", splitbelow = true, splitright = true, scrolloff = 8, sidescrolloff = 5, foldmethod = "expr", equalalways = true, conceallevel = 0, -- Search hlsearch = false, ignorecase = true, incsearch = true, wrapscan = true, -- Persistent undo undofile = true, undodir = vim.fn.stdpath("data") .. "/undo", -- Text foramtting options. See :help fo-table. formatoptions = "tcroq1]jp", } for k, v in pairs(options) do opt[k] = v end nmap("gs", "^") nmap("gl", "$") nmap("gn", "bnext") nmap("gp", "bprev") nmap("BO", "BufOnly") nmap("f", "Telescope find_files") nmap("g", "Telescope live_grep") nmap("sd", "Telescope lsp_document_symbols") nmap("sw", "Telescope lsp_dynamic_workspace_symbols") nmap("r", "Telescope registers") nmap(";", "Telescope buffers") nmap("w", "write") nmap("n", "noa write") nmap("", "gcc") nmap("y", '"+y') -- Visual Mode Blockwise Indent. This keeps the current visual block selection -- active after changing indent with '<' or '>'. Usually the visual block -- selection is lost after you shift it, which is incredibly annoying. -- -- http://vim.wikia.com/wiki/Short_mappings_for_common_tasks vmap(">", ">gv") vmap("<", " indents in normal mode as well? nmap(">", ">>") nmap("<", "<<") -- Toggles nmap("i", "set invlist") nmap("S", "set hlsearch! hlsearch?") nmap("W", "set wrap! wrap?") autocmd({ "FileType" }, { pattern = "lua", command = "set noexpandtab" }) -- Trim trailing whitespace on save autocmd({ "BufWritePre" }, { pattern = { "*" }, command = [[%s/\s\+$//e]] }) -- Highlight current line in current window only autocmd({ "WinEnter", "BufEnter" }, { pattern = "*", command = "setlocal cursorline" }) autocmd({ "WinLeave" }, { pattern = "*", command = "setlocal nocursorline" }) -- Keep splits equal size autocmd("VimResized", { pattern = "*", command = "wincmd =" }) -- Markdown spell checking augroup("markdownSpell", { clear = true }) autocmd({ "FileType" }, { pattern = "markdown", command = "setlocal spell", group = "markdownSpell", }) autocmd({ "BufRead", "BufNewFile" }, { pattern = "*.md", command = "setlocal spell", group = "markdownSpell", }) -- Reload colorscheme on save augroup("reloadColorscheme", { clear = true }) autocmd({ "BufWritePost" }, { pattern = "*/nvim/colors/charmtone.vim", command = "colorscheme charmtone", group = "reloadColorscheme", }) -- Folding with TreeSitter vim.o.foldmethod = "expr" vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" vim.o.foldlevelstart = 99 -- start with all folds open -- Blast local blast_ok, blast = pcall(require, "blast") if blast_ok then blast.setup({ socket_path = vim.fn.expand("$HOME/.local/share/blastd/blastd.sock"), idle_timeout = 120, debounce_ms = 1000, debug = false, }) end -- TreeSitter require("nvim-treesitter").install({ "bash", "css", "elm", "go", "gomod", "gowork", "haskell", "html", "javascript", "json", "lua", "scss", "templ", "typescript", "yaml", }) augroup("treesitterStart", { clear = true }) autocmd({ "FileType" }, { callback = function(args) pcall(vim.treesitter.start, args.buf) end, group = "treesitterStart", }) -- Colorizer require("colorizer").setup({ names = false }) -- Tree nmap("t", "NvimTreeToggle") require("nvim-tree").setup({ disable_netrw = true, renderer = { icons = { symlink_arrow = " >> ", show = { git = true, folder = true, file = false, folder_arrow = false, }, glyphs = { folder = { arrow_open = "", arrow_closed = "", default = "▶", open = "▼", empty = "▼", empty_open = "▼", symlink = "*", symlink_open = "!", }, git = { unstaged = "!", staged = "✓", unmerged = "U", renamed = "➜", untracked = "*", deleted = "✗", ignored = "◌", }, }, }, indent_markers = { enable = true, icons = { corner = "╰ ", edge = "│ ", none = " ", }, }, }, }) -- Telescope require("telescope").setup({ defaults = { mappings = { i = { [""] = require("telescope.actions").delete_buffer }, }, file_ignore_patterns = { "vendor/" }, }, }) -- Trouble require("trouble").setup() nmap("E", "Trouble diagnostics toggle") nmap("xx", "Trouble diagnostics toggle filter.buf=0") -- GitGutter g.gitgutter_sign_modified = "•" -- Vsnip do -- Expand map({ "i", "s" }, "", function() return vim.fn["vsnip#expandable"]() == 1 and "(vsnip-expand)" or "" end, { expr = true, silent = true }) -- Expand or jump map({ "i", "s" }, "", function() return vim.fn == 1 and "(vsnip-expand-or-jump)" or "" end, { expr = true, silent = true }) -- Jump forward map({ "i", "s" }, "", function() return vim.fn == 1 and "(vsnip-jump-next)" or "" end, { expr = true, silent = true }) -- Jump backward map({ "i", "s" }, "", function() return vim.fn["vsnip#jumpable"](-1) == 1 and "(vsnip-jump-prev)" or "" end, { expr = true, silent = true }) -- Select or cut text to use as $TM_SELECTED_TEXT in the next snippet map({ "n", "x" }, "s", "(vsnip-select-text)", { silent = true }) map({ "n", "x" }, "S", "(vsnip-cut-text)", { silent = true }) end -- Blink require("blink.cmp").setup({ completion = { list = { selection = { preselect = true, auto_insert = true, }, }, menu = { -- Don't show completion menu automatically when searching auto_show = function(ctx) return ctx.mode ~= "cmdline" or not vim.tbl_contains({ "/", "?" }, vim.fn.getcmdtype()) end, draw = { columns = { { "label", "label_description", gap = 1 }, { "kind", "source_name" }, }, }, }, documentation = { auto_show = true, auto_show_delay_ms = 250, treesitter_highlighting = true, }, }, cmdline = { enabled = true, completion = { menu = { auto_show = true } }, }, fuzzy = { implementation = "lua" }, sources = { providers = { lsp = { min_keyword_length = 1, score_offset = 0 }, path = { min_keyword_length = 0 }, buffer = { min_keyword_length = 1, max_items = 5 }, }, }, }) -- Conform do require("conform").setup({ formatters_by_ft = { cabal = { "cabal_fmt" }, css = { "prettier" }, haskell = { "fourmolu" }, html = { "prettier" }, javascript = { "prettier" }, json = { "jq" }, lua = { "stylua" }, markdown = { "prettier" }, nix = { "alejandra" }, scss = { "prettier" }, sh = { "shfmt" }, templ = { "templ" }, yaml = { "prettier" }, -- ["_"] = { "trim_whitespace", "trim_newlines" }, }, format_on_save = { timeout_ms = 500, lsp_format = "fallback", }, }) vim.o.formatexpr = "v:lua.require'conform'.formatexpr()" end -- Neocrush local ok, neocrush = pcall(require, "neocrush") if ok then neocrush.setup({ highlight_group = "IncSearch", highlight_duration = 900, auto_focus = true, terminal_width = 80, terminal_cmd = "crush", }) end -- LSP do local lspMethods = vim.lsp.protocol.Methods local capabilities = require("blink.cmp").get_lsp_capabilities({ workspace = { didChangeWatchedFiles = { dynamicRegistration = true, -- needs fswatch on linux relativePatternSupport = true, }, }, }, true) local organize_imports = function(client, bufnr, timeoutms) local params = vim.lsp.util.make_range_params() params.context = { only = { "source.organizeImports" } } local result = client.request_sync(lspMethods.textDocument_codeAction, params, timeoutms, bufnr) or {} for _, r in pairs(result.result or {}) do if r.edit then local enc = client.offset_encoding or "utf-16" vim.lsp.util.apply_workspace_edit(r.edit, enc) elseif r.command and r.command.command then vim.lsp.buf.execute_command(r.command) end end end local lspAttachGroup = vim.api.nvim_create_augroup("LspAttachGroup", { clear = true }) vim.api.nvim_create_autocmd("LspAttach", { callback = function(args) local bufnr = args.buf -- Defensive: buffer may have been deleted before vim.schedule callback runs if not vim.api.nvim_buf_is_valid(bufnr) then return end local client = vim.lsp.get_client_by_id(args.data.client_id) if client == nil then return end local map = function(key, cmd) vim.api.nvim_buf_set_keymap(bufnr, "n", key, "lua " .. cmd .. "", { noremap = true, silent = true, }) end map("gd", "vim.lsp.buf.definition()") map("lr", "vim.lsp.buf.rename()") map("a", "vim.lsp.buf.code_action()") map("ll", "vim.diagnostic.open_float()") if client:supports_method(lspMethods.textDocument_codelens) then map("lh", "vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled())") end if client:supports_method(lspMethods.textDocument_codeAction) then if client.name ~= "lua_ls" then -- organize_imports is comically broken in lua local organize_auid = vim.api.nvim_create_autocmd({ "BufWritePre" }, { buffer = bufnr, callback = function() organize_imports(client, bufnr, 1000) end, group = lspAttachGroup, }) -- Clean up the autocmd when LSP detaches to avoid nil buf_state errors vim.api.nvim_create_autocmd("LspDetach", { buffer = bufnr, callback = function(args) if args.data.client_id == client.id then vim.api.nvim_del_autocmd(organize_auid) end end, group = lspAttachGroup, once = true, }) end end end, }) vim.lsp.config("*", { capabilities = capabilities, root_markers = { ".git" }, }) for _, server in ipairs({ { name = "bashls", cmd = { "bash-language-server", "start" }, filetypes = { "sh", "bash" }, }, { name = "golangci_lint_ls", cmd = { "golangci-lint-langserver" }, filetypes = { "go", "gomod" }, }, { name = "hls", cmd = { "haskell-language-server-wrapper", "--lsp" }, filetypes = { "haskell", "lhaskell" }, }, { name = "html", cmd = { "vscode-html-language-server", "--stdio" }, filetypes = { "html" }, }, { name = "jsonls", cmd = { "vscode-json-language-server", "--stdio" }, filetypes = { "json", "jsonc" }, }, { name = "nil_ls", cmd = { "nil" }, filetypes = { "nix" }, }, { name = "templ", cmd = { "templ", "lsp" }, filetypes = { "templ" }, }, { name = "ts_ls", cmd = { "typescript-language-server", "--stdio" }, filetypes = { "javascript", "typescript" }, }, { name = "yamlls", cmd = { "yaml-language-server", "--stdio" }, filetypes = { "yaml", "yaml.docker-compose" }, }, }) do vim.lsp.config(server.name, { cmd = server.cmd, filetypes = server.filetypes, }) vim.lsp.enable(server.name) end vim.lsp.config("gopls", { cmd = { "gopls" }, filetypes = { "go", "gomod", "gowork", "gotmpl" }, root_markers = { "go.work", "go.mod", ".git" }, settings = { gopls = { gofumpt = true, codelenses = { gc_details = true, generate = true, regenerate_cgo = true, run_govulncheck = true, test = true, tidy = true, upgrade_dependency = true, vendor = true, }, hints = { assignVariableTypes = true, compositeLiteralFields = true, compositeLiteralTypes = true, constantValues = true, functionTypeParameters = true, parameterNames = true, rangeVariableTypes = true, }, analyses = { nilness = true, shadow = true, shift = true, unusedparams = true, unusedwrite = true, useany = true, unusedvariable = true, }, usePlaceholders = true, completeUnimported = true, staticcheck = true, directoryFilters = { "-.git", "-node_modules" }, }, }, }) vim.lsp.enable("gopls") vim.lsp.config("lua_ls", { cmd = { "lua-language-server" }, filetypes = { "lua" }, root_markers = { ".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml", "selene.toml", "selene.yml", ".git", }, settings = { Lua = { diagnostics = { globals = { "vim" } }, completion = { callSnippet = "Replace" }, hint = { enable = true }, }, }, }) vim.lsp.enable("lua_ls") end