vim.lsp.inlay_hint.enable(true) vim.api.nvim_create_autocmd("LspAttach", { group = vim.api.nvim_create_augroup("jdkaplan.lsp", {}), callback = function(args) local client = assert(vim.lsp.get_client_by_id(args.data.client_id)) if client:supports_method("textDocument/completion") then vim.lsp.completion.enable(true, client.id, args.buf, { autotrigger = false }) end -- Usually not needed if server supports "textDocument/willSaveWaitUntil". already_waits = client:supports_method("textDocument/willSaveWaitUntil") can_fmt = client:supports_method("textDocument/formatting") if not already_waits and can_fmt then vim.api.nvim_create_autocmd("BufWritePre", { group = vim.api.nvim_create_augroup("jdkaplan.lsp", { clear = false }), buffer = args.buf, callback = function() vim.lsp.buf.format({ bufnr = args.buf, id = client.id, timeout_ms = 1000 }) end, }) end end, }) vim.lsp.config("eslint", { on_attach = function(client, bufnr) vim.api.nvim_create_autocmd("BufWritePre", { callback = function() client:request_sync("workspace/executeCommand", { command = "eslint.applyAllFixes", arguments = { { uri = vim.uri_from_bufnr(bufnr), version = vim.lsp.util.buf_versions[bufnr], }, }, }) end, }) end, }) return { -- HUD { "preservim/nerdtree", keys = { { "t", ":NERDTreeToggle" }, { "a", ":NERDTreeFind" }, }, }, { "stevearc/aerial.nvim", dependencies = { "nvim-treesitter/nvim-treesitter", }, keys = { { "m", "AerialToggle" }, { "n", "AerialNavToggle" }, }, config = true, }, { "wellle/context.vim", lazy = false, init = function() vim.g.context_enabled = 0 vim.keymap.set("", "cc", ":ContextToggleWindow", { silent = true }) vim.keymap.set("", "cp", ":ContextPeek", { silent = true }) end, }, { "lewis6991/gitsigns.nvim", commit = "220446c8c86a280180d852efac60991eaf1a21d4", lazy = false, init = function() local gitsigns = require("gitsigns") local toggle = function(value) -- BUG: (?) If the blame delay is not zero, the setting can -- enter a race with cursor movement and leave a ghost blame in -- the buffer. show = not show gitsigns.toggle_current_line_blame(show) gitsigns.toggle_deleted(show) gitsigns.toggle_signs(show) end vim.keymap.set("n", "gg", toggle) vim.keymap.set("n", "[h", gitsigns.prev_hunk) vim.keymap.set("n", "]h", gitsigns.next_hunk) end, opts = { signcolumn = false, show_deleted = false, current_line_blame = false, current_line_blame_formatter = " ", current_line_blame_opts = { virt_text = true, virt_text_pos = "eol", delay = 0, ignore_whitespace = false, }, }, }, { "RRethy/vim-illuminate", init = function() local illuminate = require("illuminate") illuminate.configure({ delay = 500, }) end, keys = { { "ii", function() require("illuminate").toggle_buf() end, }, { "in", function() require("illuminate").goto_next_reference() end, }, { "ip", function() require("illuminate").goto_prev_reference() end, }, { "if", function() require("illuminate").toggle_freeze_buf() end, }, }, }, { "lukas-reineke/indent-blankline.nvim", main = "ibl", opts = { indent = { char = "┊", }, scope = { enabled = false }, }, }, { "andythigpen/nvim-coverage", requires = { "nvim-lua/plenary.nvim" }, version = "*", config = function() require("coverage").setup({ auto_reload = true, signs = { covered = { text = "▎" }, partial = { text = "▌" }, uncovered = { text = "█" }, }, }) end, }, { "pmouraguedes/sql-ghosty.nvim", dependencies = { "nvim-treesitter/nvim-treesitter" }, opts = {}, }, { "jfryy/keytrail.nvim", url = "https://github.com/jdkaplan/keytrail.nvim", dependencies = { "nvim-treesitter/nvim-treesitter" }, opts = { key_mapping = "j", }, }, -- Treesitter { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate", init = function() require("nvim-treesitter.configs").setup({ highlight = { enable = true }, incremental_selection = { enable = true }, textobjects = { enable = true, select = { enable = true, lookahead = true, include_surrounding_whitespace = false, -- https://github.com/nvim-treesitter/nvim-treesitter-textobjects#overriding-or-extending-textobjects keymaps = { ["af"] = "@function.outer", ["if"] = "@function.inner", ["ac"] = "@comment.outer", ["ic"] = "@comment.outer", }, }, move = { enable = true, set_jumps = true, goto_next_start = { ["]m"] = "@function.outer", ["]{"] = "@block.outer", }, goto_next_end = { ["]M"] = "@function.outer", ["]}"] = "@block.outer", }, goto_previous_start = { ["[m"] = "@function.outer", ["[{"] = "@block.outer", }, goto_previous_end = { ["[M"] = "@function.outer", ["[}"] = "@block.outer", }, }, }, ensure_installed = { "bash", "css", "dot", "diff", "git_config", "git_rebase", "gitattributes", "gitcommit", "gitignore", "go", "gomod", "gosum", "gowork", "html", "javascript", "jq", "json", "lua", "markdown", "markdown_inline", "proto", "python", "ruby", "rust", "scss", "sql", "toml", "tsx", "typescript", "yaml", }, }) end, }, { "nvim-treesitter/nvim-treesitter-textobjects", dependencies = { "nvim-treesitter/nvim-treesitter", }, }, { "windwp/nvim-ts-autotag", opts = { autotag = { enabled = true, enable_close_on_slash = false, }, }, }, -- Telescope { "nvim-telescope/telescope.nvim", version = "^0.1.0", dependencies = { "nvim-lua/plenary.nvim", "nvim-telescope/telescope-fzf-native.nvim", "nvim-telescope/telescope-ui-select.nvim", "MunifTanjim/nui.nvim", "stevearc/aerial.nvim", "nvim-telescope/telescope-symbols.nvim", }, init = function() local telescope = require("telescope") telescope.load_extension("fzf") telescope.load_extension("aerial") telescope.load_extension("ui-select") local builtin = require("telescope.builtin") local function find_files(opts) opts = opts or {} opts["hidden"] = true return builtin.find_files(opts) end vim.keymap.set("n", "f", find_files) vim.keymap.set("n", "g", builtin.live_grep) vim.keymap.set("n", "b", builtin.buffers) vim.keymap.set("n", "*", builtin.grep_string) vim.keymap.set("n", "o", telescope.extensions.aerial.aerial) vim.keymap.set("n", "t", builtin.builtin) -- TODO: Delete these ; fake-leader bindings vim.keymap.set("n", ";f", find_files) vim.keymap.set("n", ";g", builtin.live_grep) vim.keymap.set("n", ";b", builtin.buffers) vim.keymap.set("n", ";*", builtin.grep_string) vim.keymap.set("i", "", builtin.symbols) end, opts = function(_plugin, _config) local actions = require("telescope.actions") local action_state = require("telescope.actions.state") local transform_mod = require("telescope.actions.mt").transform_mod -- https://github.com/nvim-telescope/telescope.nvim/issues/1048#issuecomment-1225975038 local function multiopen(prompt_bufnr, method) local method = method or "default" local edit_file_cmd_map = { vertical = "vsplit", horizontal = "split", tab = "tabedit", default = "edit", } local edit_buf_cmd_map = { vertical = "vert sbuffer", horizontal = "sbuffer", tab = "tab sbuffer", default = "buffer", } local picker = action_state.get_current_picker(prompt_bufnr) local multi_selection = picker:get_multi_selection() if #multi_selection > 1 then require("telescope.pickers").on_close_prompt(prompt_bufnr) pcall(vim.api.nvim_set_current_win, picker.original_win_id) for i, entry in ipairs(multi_selection) do local filename, row, col if entry.path or entry.filename then filename = entry.path or entry.filename row = entry.row or entry.lnum col = vim.F.if_nil(entry.col, 1) elseif not entry.bufnr then local value = entry.value if not value then goto continue end if type(value) == "table" then value = entry.display end local sections = vim.split(value, ":") filename = sections[1] row = tonumber(sections[2]) col = tonumber(sections[3]) end local entry_bufnr = entry.bufnr if entry_bufnr then if not vim.api.nvim_buf_get_option(entry_bufnr, "buflisted") then vim.api.nvim_buf_set_option(entry_bufnr, "buflisted", true) end local command = i == 1 and "buffer" or edit_buf_cmd_map[method] pcall(vim.cmd, string.format("%s %s", command, vim.api.nvim_buf_get_name(entry_bufnr))) else local command = i == 1 and "edit" or edit_file_cmd_map[method] if vim.api.nvim_buf_get_name(0) ~= filename or command ~= "edit" then filename = require("plenary.path"):new(vim.fn.fnameescape(filename)):normalize(vim.loop.cwd()) pcall(vim.cmd, string.format("%s %s", command, filename)) end end if row and col then pcall(vim.api.nvim_win_set_cursor, 0, { row, col - 1 }) end ::continue:: end else actions["select_" .. method](prompt_bufnr) end end return { defaults = { mappings = { i = { [""] = multiopen, }, n = { [""] = multiopen, }, }, vimgrep_arguments = { "rg", "--color=never", "--no-heading", "--with-filename", "--line-number", "--column", "--smart-case", -- Include hidden files, but continue ignoring the .git directory itself. "--hidden", "--iglob", "!.git", }, }, pickers = {}, extensions = { aerial = {}, }, } end, }, { "nvim-telescope/telescope-fzf-native.nvim", build = "make", }, -- LSP { "mrcjkb/rustaceanvim", version = "^6", lazy = false, }, { "mason-org/mason-lspconfig.nvim", opts = {}, dependencies = { "neovim/nvim-lspconfig", { "mason-org/mason.nvim", opts = { PATH = "append", }, }, }, }, -- Completion + snippets { "hrsh7th/cmp-nvim-lsp", branch = "main" }, { "hrsh7th/nvim-cmp", branch = "main" }, { "saadparwaiz1/cmp_luasnip", dependencies = { "L3MON4D3/LuaSnip", }, }, { "L3MON4D3/LuaSnip", dependencies = { "hrsh7th/nvim-cmp", "hrsh7th/cmp-nvim-lsp", }, build = "make install_jsregexp", init = function() local luasnip = require("luasnip") luasnip.filetype_extend("typescript", { "javascript" }) luasnip.filetype_extend("typescriptreact", { "javascript", "react" }) -- Put snippets in ./snippets/.snippets require("luasnip.loaders.from_snipmate").lazy_load() require("luasnip.loaders.from_lua").lazy_load() local cmp = require("cmp") cmp.setup({ completion = { autocomplete = false, }, snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, mapping = cmp.mapping.preset.insert({ [""] = cmp.mapping.scroll_docs(4), [""] = cmp.mapping.scroll_docs(-4), [""] = cmp.mapping.complete(), [""] = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = true, }), [""] = cmp.mapping(function(fallback) if cmp.visible() then cmp.select_next_item() elseif luasnip.expand_or_jumpable() then luasnip.expand_or_jump() else fallback() end end, { "i", "s" }), [""] = cmp.mapping(function(fallback) if cmp.visible() then cmp.select_prev_item() elseif luasnip.jumpable(-1) then luasnip.jump(-1) else fallback() end end, { "i", "s" }), }), sources = { { name = "nvim_lsp" }, { name = "luasnip" }, }, }) end, }, -- Debugging { "mfussenegger/nvim-dap", lazy = false, keys = { { "db", function() require("dap").toggle_breakpoint() end, }, { "dB", function() require("dap").set_breakpoint() end, }, { "dc", function() require("dap").continue() end, }, { "dn", function() require("dap").step_over() end, }, { "dsi", function() require("dap").step_into() end, }, { "dso", function() require("dap").step_out() end, }, { "dj", function() require("dap").down() end, }, { "dk", function() require("dap").up() end, }, }, }, }