-- Bootstrap Lazy.nvim local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not vim.loop.fs_stat(lazypath) then local result = vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", "--branch=stable", -- latest stable release lazypath }) if vim.v.shell_error ~= 0 then vim.api.nvim_echo({ { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, { result, "WarningMsg" }, { "\nPress any key to exit...", "MoreMsg" }, }, true, {}) vim.fn.getchar() os.exit(1) end end vim.opt.rtp:prepend(lazypath) -- Leader key must be set before lazy setup vim.g.mapleader = " " vim.g.maplocalleader = "\\" -- Core vim settings vim.opt.number = true vim.opt.relativenumber = true vim.opt.wrap = false vim.opt.expandtab = true vim.opt.shiftwidth = 2 vim.opt.tabstop = 2 vim.opt.softtabstop = 2 vim.opt.smartindent = true vim.opt.cursorline = true vim.opt.termguicolors = true vim.opt.signcolumn = "yes" vim.opt.updatetime = 250 vim.opt.timeoutlen = 300 vim.opt.splitright = true vim.opt.splitbelow = true vim.opt.undofile = true vim.opt.ignorecase = true vim.opt.smartcase = true vim.opt.mouse = "a" vim.opt.clipboard = "unnamedplus" vim.o.shortmess = vim.o.shortmess .. "I" -- Plugin specifications require("lazy").setup({ -- LSP and completion setup { "neovim/nvim-lspconfig", event = { "BufReadPre", "BufNewFile" }, dependencies = { "williamboman/mason.nvim", "williamboman/mason-lspconfig.nvim", "hrsh7th/cmp-nvim-lsp", "folke/neodev.nvim", }, config = function() -- Setup neodev before lspconfig require("neodev").setup() -- Diagnostic configuration vim.diagnostic.config({ virtual_text = { prefix = "●", }, signs = true, underline = true, update_in_insert = false, severity_sort = true, }) -- Diagnostic signs local signs = { Error = " ", Warn = " ", Hint = "󰠠 ", Info = " " } for type, icon in pairs(signs) do local hl = "DiagnosticSign" .. type vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = "" }) end -- LSP keymaps local on_attach = function(client, bufnr) local opts = { buffer = bufnr, silent = true } local keymap = vim.keymap.set keymap("n", "gD", vim.lsp.buf.declaration, opts) keymap("n", "gd", vim.lsp.buf.definition, opts) keymap("n", "K", vim.lsp.buf.hover, opts) keymap("n", "gi", vim.lsp.buf.implementation, opts) keymap("n", "", vim.lsp.buf.signature_help, opts) keymap("n", "wa", vim.lsp.buf.add_workspace_folder, opts) keymap("n", "wr", vim.lsp.buf.remove_workspace_folder, opts) keymap("n", "wl", function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts) keymap("n", "D", vim.lsp.buf.type_definition, opts) keymap("n", "rn", vim.lsp.buf.rename, opts) keymap("n", "ca", vim.lsp.buf.code_action, opts) keymap("n", "gr", vim.lsp.buf.references, opts) keymap("n", "f", function() vim.lsp.buf.format({ async = true }) end, opts) end local capabilities = require("cmp_nvim_lsp").default_capabilities() local lspconfig = require("lspconfig") -- Setup Mason require("mason").setup({ ui = { icons = { package_installed = "✓", package_pending = "➜", package_uninstalled = "✗", }, }, }) require("mason-lspconfig").setup({ ensure_installed = { "lua_ls", "pyright", "clangd", "html", "cssls", "ts_ls", }, automatic_installation = true, }) -- LSP server configurations local servers = { lua_ls = { settings = { Lua = { workspace = { checkThirdParty = false, }, completion = { callSnippet = "Replace", }, }, }, }, pyright = {}, clangd = { cmd = { "clangd", "--background-index", "--clang-tidy" }, }, html = { filetypes = { "html", "htmldjango" }, }, cssls = {}, ts_ls = {}, } require("mason-lspconfig").setup_handlers({ function(server_name) local server = servers[server_name] or {} server.capabilities = vim.tbl_deep_extend("force", {}, capabilities, server.capabilities or {}) server.on_attach = on_attach lspconfig[server_name].setup(server) end, }) end, }, -- Mason for managing external tools { "williamboman/mason.nvim", cmd = "Mason", keys = { { "cm", "Mason", desc = "Mason" } }, }, -- Autocompletion { "hrsh7th/nvim-cmp", version = false, event = "InsertEnter", dependencies = { "hrsh7th/cmp-nvim-lsp", "hrsh7th/cmp-buffer", "hrsh7th/cmp-path", "saadparwaiz1/cmp_luasnip", }, opts = function() vim.api.nvim_set_hl(0, "CmpGhostText", { link = "Comment", default = true }) local cmp = require("cmp") local luasnip = require("luasnip") return { completion = { completeopt = "menu,menuone,noinsert", }, snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, mapping = cmp.mapping.preset.insert({ [""] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }), [""] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }), [""] = cmp.mapping.scroll_docs(-4), [""] = cmp.mapping.scroll_docs(4), [""] = cmp.mapping.complete(), [""] = cmp.mapping.abort(), [""] = cmp.mapping.confirm({ 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 = cmp.config.sources({ { name = "nvim_lsp" }, { name = "luasnip" }, { name = "path" }, }, { { name = "buffer" }, }), formatting = { format = function(entry, item) local icons = { nvim_lsp = "", luasnip = "", buffer = "", path = "", } item.menu = icons[entry.source.name] return item end, }, experimental = { ghost_text = { hl_group = "CmpGhostText", }, }, } end, }, -- Snippets { "L3MON4D3/LuaSnip", build = (function() if vim.fn.has("win32") == 1 or vim.fn.executable("make") == 0 then return end return "make install_jsregexp" end)(), dependencies = { "rafamadriz/friendly-snippets", config = function() require("luasnip.loaders.from_vscode").lazy_load() end, }, opts = { history = true, delete_check_events = "TextChanged", }, }, -- Neodev for better lua development { "folke/neodev.nvim", opts = {} }, -- Treesitter { "nvim-treesitter/nvim-treesitter", version = false, build = ":TSUpdate", event = { "BufReadPost", "BufNewFile" }, cmd = { "TSUpdateSync" }, opts = { ensure_installed = { "bash", "c", "cpp", "css", "html", "javascript", "json", "lua", "markdown", "markdown_inline", "python", "query", "regex", "tsx", "typescript", "vim", "yaml", }, highlight = { enable = true }, indent = { enable = true }, incremental_selection = { enable = true, keymaps = { init_selection = "", node_incremental = "", scope_incremental = false, node_decremental = "", }, }, }, config = function(_, opts) require("nvim-treesitter.configs").setup(opts) end, }, -- Auto tag { "windwp/nvim-ts-autotag", event = "InsertEnter", opts = {}, }, -- Emmet { "mattn/emmet-vim", ft = { "html", "css", "javascript", "typescript", "javascriptreact", "typescriptreact" }, init = function() vim.g.user_emmet_leader_key = "" end, }, -- Colorscheme { "folke/tokyonight.nvim", lazy = false, priority = 1000, opts = { style = "night", transparent = false, terminal_colors = true, styles = { comments = { italic = true }, keywords = { italic = true }, functions = {}, variables = {}, sidebars = "dark", floats = "dark", }, }, config = function(_, opts) require("tokyonight").setup(opts) vim.cmd.colorscheme("tokyonight") end, }, -- Statusline { "nvim-lualine/lualine.nvim", event = "VeryLazy", opts = function() return { options = { theme = "tokyonight", globalstatus = true, disabled_filetypes = { statusline = { "dashboard", "alpha" } }, }, sections = { lualine_a = { "mode" }, lualine_b = { "branch" }, lualine_c = { { "diagnostics", symbols = { error = " ", warn = " ", info = " ", hint = "󰠠 ", }, }, { "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } }, { "filename", path = 1, symbols = { modified = " ", readonly = "", unnamed = "" } }, }, lualine_x = { { function() return require("noice").api.status.command.get() end, cond = function() return package.loaded["noice"] and require("noice").api.status.command.has() end, color = { fg = "#ff9e64" }, }, { function() return require("noice").api.status.mode.get() end, cond = function() return package.loaded["noice"] and require("noice").api.status.mode.has() end, color = { fg = "#ff9e64" }, }, { "encoding" }, { "fileformat" }, }, lualine_y = { "progress" }, lualine_z = { "location" }, }, extensions = { "neo-tree", "lazy" }, } end, }, -- File explorer { "nvim-neo-tree/neo-tree.nvim", branch = "v3.x", cmd = "Neotree", keys = { { "fe", function() require("neo-tree.command").execute({ toggle = true, dir = vim.loop.cwd() }) end, desc = "Explorer NeoTree (cwd)" }, { "fE", function() require("neo-tree.command").execute({ toggle = true, dir = vim.fn.expand("%:p:h") }) end, desc = "Explorer NeoTree (current file)" }, { "e", "fe", desc = "Explorer NeoTree (cwd)", remap = true }, { "E", "fE", desc = "Explorer NeoTree (current file)", remap = true }, }, deactivate = function() vim.cmd([[Neotree close]]) end, init = function() if vim.fn.argc(-1) == 1 then local stat = vim.loop.fs_stat(vim.fn.argv(0)) if stat and stat.type == "directory" then require("neo-tree") end end end, opts = { sources = { "filesystem", "buffers", "git_status", "document_symbols" }, open_files_do_not_replace_types = { "terminal", "Trouble", "qf", "Outline" }, filesystem = { bind_to_cwd = false, follow_current_file = { enabled = true }, use_libuv_file_watcher = true, filtered_items = { visible = true, hide_dotfiles = false, hide_gitignored = false, }, }, window = { position = "left", width = 30, mappings = { [""] = "none", }, }, default_component_configs = { indent = { with_expanders = true, expander_collapsed = "", expander_expanded = "", expander_highlight = "NeoTreeExpander", }, }, }, }, -- Fuzzy finder { "nvim-telescope/telescope.nvim", cmd = "Telescope", version = false, dependencies = { "nvim-lua/plenary.nvim", { "nvim-telescope/telescope-fzf-native.nvim", build = "make", enabled = vim.fn.executable("make") == 1, config = function() require("telescope").load_extension("fzf") end, }, }, keys = { { ",", "Telescope buffers show_all_buffers=true", desc = "Switch Buffer" }, { "/", "Telescope live_grep", desc = "Grep (root dir)" }, { ":", "Telescope command_history", desc = "Command History" }, { "", "Telescope find_files", desc = "Find Files (root dir)" }, { "fb", "Telescope buffers", desc = "Buffers" }, { "ff", "Telescope find_files", desc = "Find Files (root dir)" }, { "fF", "Telescope find_files hidden=true no_ignore=true", desc = "Find Files (all)" }, { "fg", "Telescope live_grep", desc = "Grep (root dir)" }, { "fh", "Telescope help_tags", desc = "Help Pages" }, { "fH", "Telescope highlights", desc = "Search Highlight Groups" }, { "fk", "Telescope keymaps", desc = "Key Maps" }, { "fl", "Telescope loclist", desc = "Location List" }, { "fM", "Telescope man_pages", desc = "Man Pages" }, { "fm", "Telescope marks", desc = "Jump to Mark" }, { "fo", "Telescope vim_options", desc = "Options" }, { "fR", "Telescope resume", desc = "Resume" }, { "fq", "Telescope quickfix", desc = "Quickfix List" }, { "fw", "Telescope grep_string word_match=-w", desc = "Word (root dir)" }, { "fW", "Telescope grep_string", desc = "Selection (root dir)", mode = "v" }, { "uC", "Telescope colorscheme enable_preview=true", desc = "Colorscheme with preview" }, }, opts = function() return { defaults = { file_ignore_patterns = { "node_modules", ".git/" }, vimgrep_arguments = { "rg", "-L", "--color=never", "--no-heading", "--with-filename", "--line-number", "--column", "--smart-case", }, prompt_prefix = " ", selection_caret = " ", entry_prefix = " ", initial_mode = "insert", selection_strategy = "reset", sorting_strategy = "ascending", layout_strategy = "horizontal", layout_config = { horizontal = { prompt_position = "top", preview_width = 0.55, results_width = 0.8, }, vertical = { mirror = false, }, width = 0.87, height = 0.80, preview_cutoff = 120, }, path_display = { "truncate" }, winblend = 0, border = {}, borderchars = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, color_devicons = true, set_env = { ["COLORTERM"] = "truecolor" }, }, pickers = { live_grep = { theme = "dropdown", }, grep_string = { theme = "dropdown", }, find_files = { theme = "dropdown", previewer = false, }, buffers = { theme = "dropdown", previewer = false, initial_mode = "normal", mappings = { i = { [""] = function(...) return require("telescope.actions").delete_buffer(...) end, }, n = { ["dd"] = function(...) return require("telescope.actions").delete_buffer(...) end, }, }, }, planets = { show_pluto = true, show_moon = true, }, colorscheme = { enable_preview = true, }, }, } end, }, -- Better vim.ui { "stevearc/dressing.nvim", lazy = true, init = function() vim.ui.select = function(...) require("lazy").load({ plugins = { "dressing.nvim" } }) return vim.ui.select(...) end vim.ui.input = function(...) require("lazy").load({ plugins = { "dressing.nvim" } }) return vim.ui.input(...) end end, }, -- Auto pairs { "windwp/nvim-autopairs", event = "InsertEnter", opts = {}, }, -- Comments { "numToStr/Comment.nvim", opts = {}, lazy = false, }, -- Which key { "folke/which-key.nvim", event = "VeryLazy", opts = { plugins = { spelling = true }, defaults = { mode = { "n", "v" }, ["g"] = { name = "+goto" }, ["gz"] = { name = "+surround" }, ["]"] = { name = "+next" }, ["["] = { name = "+prev" }, [""] = { name = "+tabs" }, ["b"] = { name = "+buffer" }, ["c"] = { name = "+code" }, ["f"] = { name = "+file/find" }, ["g"] = { name = "+git" }, ["gh"] = { name = "+hunks" }, ["q"] = { name = "+quit/session" }, ["s"] = { name = "+search" }, ["u"] = { name = "+ui" }, ["w"] = { name = "+windows" }, ["x"] = { name = "+diagnostics/quickfix" }, }, }, config = function(_, opts) local wk = require("which-key") wk.setup(opts) wk.register(opts.defaults) end, }, -- Git signs { "lewis6991/gitsigns.nvim", event = { "BufReadPre", "BufNewFile" }, opts = { signs = { add = { text = "▎" }, change = { text = "▎" }, delete = { text = "" }, topdelete = { text = "" }, changedelete = { text = "▎" }, untracked = { text = "▎" }, }, on_attach = function(buffer) local gs = package.loaded.gitsigns local function map(mode, l, r, desc) vim.keymap.set(mode, l, r, { buffer = buffer, desc = desc }) end map("n", "]h", gs.next_hunk, "Next Hunk") map("n", "[h", gs.prev_hunk, "Prev Hunk") map({ "n", "v" }, "ghs", ":Gitsigns stage_hunk", "Stage Hunk") map({ "n", "v" }, "ghr", ":Gitsigns reset_hunk", "Reset Hunk") map("n", "ghS", gs.stage_buffer, "Stage Buffer") map("n", "ghu", gs.undo_stage_hunk, "Undo Stage Hunk") map("n", "ghR", gs.reset_buffer, "Reset Buffer") map("n", "ghp", gs.preview_hunk, "Preview Hunk") map("n", "ghb", function() gs.blame_line({ full = true }) end, "Blame Line") map("n", "ghd", gs.diffthis, "Diff This") map("n", "ghD", function() gs.diffthis("~") end, "Diff This ~") map({ "o", "x" }, "ih", ":Gitsigns select_hunk", "GitSigns Select Hunk") end, }, }, -- Dashboard { "nvimdev/dashboard-nvim", event = "VimEnter", opts = function() local logo = [[ ███╗ ██╗███████╗ ██████╗ ██╗ ██╗██╗███╗ ███╗ ████╗ ██║██╔════╝██╔═══██╗██║ ██║██║████╗ ████║ ██╔██╗ ██║█████╗ ██║ ██║██║ ██║██║██╔████╔██║ ██║╚██╗██║██╔══╝ ██║ ██║╚██╗ ██╔╝██║██║╚██╔╝██║ ██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║ ╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ]] logo = string.rep("\n", 8) .. logo .. "\n\n" local opts = { theme = "doom", hide = { statusline = false, }, config = { header = vim.split(logo, "\n"), center = { { action = "Telescope find_files", desc = " Find file", icon = " ", key = "f" }, { action = "ene | startinsert", desc = " New file", icon = " ", key = "n" }, { action = "Telescope oldfiles", desc = " Recent files", icon = " ", key = "r" }, { action = "Telescope live_grep", desc = " Find text", icon = " ", key = "g" }, { action = "e $MYVIMRC", desc = " Config", icon = " ", key = "c" }, { action = "Lazy", desc = " Lazy", icon = "󰒲 ", key = "l" }, { action = "qa", desc = " Quit", icon = " ", key = "q" }, }, footer = function() local stats = require("lazy").stats() local ms = (math.floor(stats.startuptime * 100 + 0.5) / 100) return { "⚡ Neovim loaded " .. stats.loaded .. "/" .. stats.count .. " plugins in " .. ms .. "ms" } end, }, } for _, button in ipairs(opts.config.center) do button.desc = button.desc .. string.rep(" ", 43 - #button.desc) button.key_format = " %s" end if vim.o.filetype == "lazy" then vim.cmd.close() vim.api.nvim_create_autocmd("User", { pattern = "DashboardLoaded", callback = function() require("lazy").show() end, }) end return opts end, }, -- Formatting { "stevearc/conform.nvim", dependencies = { "mason.nvim" }, lazy = true, cmd = "ConformInfo", keys = { { "cF", function() require("conform").format({ formatters = { "injected" } }) end, mode = { "n", "v" }, desc = "Format Injected Langs", }, }, init = function() vim.o.formatexpr = "v:lua.require'conform'.formatexpr()" end, opts = { formatters_by_ft = { lua = { "stylua" }, python = { "isort", "black" }, javascript = { { "prettierd", "prettier" } }, typescript = { { "prettierd", "prettier" } }, html = { { "prettierd", "prettier" } }, css = { { "prettierd", "prettier" } }, json = { { "prettierd", "prettier" } }, yaml = { { "prettierd", "prettier" } }, markdown = { { "prettierd", "prettier" } }, cpp = { "clang-format" }, c = { "clang-format" }, }, format_on_save = function(bufnr) if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then return end local filetype = vim.bo[bufnr].filetype if filetype == "c" or filetype == "cpp" then return { timeout_ms = 500 } end return { timeout_ms = 500, lsp_fallback = true } end, formatters = { injected = { options = { ignore_errors = true } }, }, }, }, -- Linting { "mfussenegger/nvim-lint", event = { "BufReadPre", "BufNewFile" }, opts = { events = { "BufWritePost", "BufReadPost", "InsertLeave" }, linters_by_ft = { python = { "flake8" }, cpp = { "cpplint" }, c = { "cpplint" }, javascript = { "eslint_d" }, typescript = { "eslint_d" }, }, }, config = function(_, opts) local M = {} local lint = require("lint") for name, linter in pairs(opts.linters or {}) do if type(linter) == "table" and type(lint.linters[name]) == "table" then lint.linters[name] = vim.tbl_deep_extend("force", lint.linters[name], linter) else lint.linters[name] = linter end end lint.linters_by_ft = opts.linters_by_ft function M.debounce(ms, fn) local timer = vim.loop.new_timer() return function(...) local argv = { ... } timer:start(ms, 0, function() timer:stop() vim.schedule_wrap(fn)(unpack(argv)) end) end end function M.lint() local names = lint._resolve_linter_by_ft(vim.bo.filetype) names = vim.list_extend({}, names) if #names == 0 then vim.list_extend(names, lint.linters_by_ft["_"] or {}) end vim.list_extend(names, lint.linters_by_ft["*"] or {}) local ctx = { filename = vim.api.nvim_buf_get_name(0) } ctx.dirname = vim.fn.fnamemodify(ctx.filename, ":h") names = vim.tbl_filter(function(name) local linter = lint.linters[name] if not linter then return false end return not (type(linter) == "table" and linter.condition and not linter.condition(ctx)) end, names) if #names > 0 then lint.try_lint(names) end end vim.api.nvim_create_autocmd(opts.events, { group = vim.api.nvim_create_augroup("nvim-lint", { clear = true }), callback = M.debounce(100, M.lint), }) end, }, -- Live server for web development { "barrett-ruth/live-server.nvim", build = "npm install -g live-server", cmd = { "LiveServerStart", "LiveServerStop" }, config = true, }, -- Debugging { "mfussenegger/nvim-dap", dependencies = { "rcarriga/nvim-dap-ui", "nvim-neotest/nvim-nio", "williamboman/mason.nvim", "jay-babu/mason-nvim-dap.nvim", }, keys = function() local dap = require("dap") local dapui = require("dapui") return { { "dB", function() dap.set_breakpoint(vim.fn.input('Breakpoint condition: ')) end, desc = "Breakpoint Condition" }, { "db", function() dap.toggle_breakpoint() end, desc = "Toggle Breakpoint" }, { "dc", function() dap.continue() end, desc = "Continue" }, { "dC", function() dap.run_to_cursor() end, desc = "Run to Cursor" }, { "dg", function() dap.goto_() end, desc = "Go to line (no execute)" }, { "di", function() dap.step_into() end, desc = "Step Into" }, { "dj", function() dap.down() end, desc = "Down" }, { "dk", function() dap.up() end, desc = "Up" }, { "dl", function() dap.run_last() end, desc = "Run Last" }, { "do", function() dap.step_out() end, desc = "Step Out" }, { "dO", function() dap.step_over() end, desc = "Step Over" }, { "dp", function() dap.pause() end, desc = "Pause" }, { "dr", function() dap.repl.toggle() end, desc = "Toggle REPL" }, { "ds", function() dap.session() end, desc = "Session" }, { "dt", function() dap.terminate() end, desc = "Terminate" }, { "dw", function() require("dap.ui.widgets").hover() end, desc = "Widgets" }, { "", function() dap.continue() end, desc = "Debug: Start/Continue" }, { "", function() dap.step_over() end, desc = "Debug: Step Over" }, { "", function() dap.step_into() end, desc = "Debug: Step Into" }, { "", function() dap.step_out() end, desc = "Debug: Step Out" }, } end, config = function() local dap = require("dap") local dapui = require("dapui") require("mason-nvim-dap").setup({ automatic_setup = true, handlers = {}, ensure_installed = { "python", "codelldb", }, }) dapui.setup({ icons = { expanded = "▾", collapsed = "▸", current_frame = "*" }, controls = { icons = { pause = "⏸", play = "▶", step_into = "⏎", step_over = "⏭", step_out = "⏮", step_back = "b", run_last = "▶▶", terminate = "⏹", disconnect = "⏏", }, }, }) 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 -- Install language specific config require("dap-python").setup("~/.virtualenvs/debugpy/bin/python") end, }, -- Python debugging { "mfussenegger/nvim-dap-python", ft = "python", dependencies = { "mfussenegger/nvim-dap", "rcarriga/nvim-dap-ui", }, config = function(_, opts) local path = require("mason-registry").get_package("debugpy"):get_install_path() require("dap-python").setup(path .. "/venv/bin/python") end, }, -- Better notifications { "rcarriga/nvim-notify", keys = { { "un", function() require("notify").dismiss({ silent = true, pending = true }) end, desc = "Dismiss all Notifications", }, }, opts = { timeout = 3000, max_height = function() return math.floor(vim.o.lines * 0.75) end, max_width = function() return math.floor(vim.o.columns * 0.75) end, }, init = function() vim.notify = require("notify") end, }, -- Better vim.ui { "folke/noice.nvim", event = "VeryLazy", opts = { lsp = { override = { ["vim.lsp.util.convert_input_to_markdown_lines"] = true, ["vim.lsp.util.stylize_markdown"] = true, ["cmp.entry.get_documentation"] = true, }, }, routes = { { filter = { event = "msg_show", any = { { find = "%d+L, %d+B" }, { find = "; after #%d+" }, { find = "; before #%d+" }, }, }, view = "mini", }, }, presets = { bottom_search = true, command_palette = true, long_message_to_split = true, inc_rename = true, lsp_doc_border = false, }, }, dependencies = { "MunifTanjim/nui.nvim", "rcarriga/nvim-notify", }, }, -- Buffer remove { "echasnovski/mini.bufremove", keys = { { "bd", function() require("mini.bufremove").delete(0, false) end, desc = "Delete Buffer" }, { "bD", function() require("mini.bufremove").delete(0, true) end, desc = "Delete Buffer (Force)" }, }, }, -- Session management { "folke/persistence.nvim", event = "BufReadPre", opts = { options = vim.opt.sessionoptions:get() }, keys = { { "qs", function() require("persistence").load() end, desc = "Restore Session" }, { "ql", function() require("persistence").load({ last = true }) end, desc = "Restore Last Session" }, { "qd", function() require("persistence").stop() end, desc = "Don't Save Current Session" }, }, }, }, { defaults = { lazy = false, version = false, }, install = { colorscheme = { "tokyonight", "habamax" } }, checker = { enabled = true }, performance = { rtp = { disabled_plugins = { "gzip", "tarPlugin", "tohtml", "tutor", "zipPlugin", }, }, }, }) -- Additional keymaps local keymap = vim.keymap.set local opts = { silent = true } -- Better up/down keymap({ "n", "x" }, "j", "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true }) keymap({ "n", "x" }, "k", "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true }) -- Move to window using the hjkl keys keymap("n", "", "h", { desc = "Go to left window", remap = true }) keymap("n", "", "j", { desc = "Go to lower window", remap = true }) keymap("n", "", "k", { desc = "Go to upper window", remap = true }) keymap("n", "", "l", { desc = "Go to right window", remap = true }) -- Resize window using arrow keys keymap("n", "", "resize +2", { desc = "Increase window height" }) keymap("n", "", "resize -2", { desc = "Decrease window height" }) keymap("n", "", "vertical resize -2", { desc = "Decrease window width" }) keymap("n", "", "vertical resize +2", { desc = "Increase window width" }) -- Move Lines keymap("n", "", "m .+1==", { desc = "Move down" }) keymap("n", "", "m .-2==", { desc = "Move up" }) keymap("i", "", "m .+1==gi", { desc = "Move down" }) keymap("i", "", "m .-2==gi", { desc = "Move up" }) keymap("v", "", ":m '>+1gv=gv", { desc = "Move down" }) keymap("v", "", ":m '<-2gv=gv", { desc = "Move up" }) -- Buffers keymap("n", "", "bprevious", { desc = "Prev buffer" }) keymap("n", "", "bnext", { desc = "Next buffer" }) keymap("n", "[b", "bprevious", { desc = "Prev buffer" }) keymap("n", "]b", "bnext", { desc = "Next buffer" }) -- Clear search with keymap({ "i", "n" }, "", "noh", { desc = "Escape and clear hlsearch" }) -- Save file keymap({ "i", "x", "n", "s" }, "", "w", { desc = "Save file" }) -- Better indenting keymap("v", "<", "", ">gv") -- Lazy keymap("n", "l", "Lazy", { desc = "Lazy" }) -- New file keymap("n", "fn", "enew", { desc = "New File" }) -- Quit keymap("n", "qq", "qa", { desc = "Quit all" }) -- Highlights under cursor keymap("n", "ui", vim.show_pos, { desc = "Inspect Pos" }) -- Python run keymap("n", "r", function() if vim.bo.filetype == "python" then vim.cmd("w") vim.cmd("!python3 %") else print("Not a Python file") end end, { desc = "Run Python file" }) -- Toggle options keymap("n", "uf", function() vim.g.disable_autoformat = not vim.g.disable_autoformat if vim.g.disable_autoformat then print("Disabled format on save") else print("Enabled format on save") end end, { desc = "Toggle auto format (global)" }) keymap("n", "uF", function() vim.b.disable_autoformat = not vim.b.disable_autoformat if vim.b.disable_autoformat then print("Disabled format on save (buffer)") else print("Enabled format on save (buffer)") end end, { desc = "Toggle auto format (buffer)" }) -- Auto commands local function augroup(name) return vim.api.nvim_create_augroup("lazyvim_" .. name, { clear = true }) end -- Check if we need to reload the file when it changed vim.api.nvim_create_autocmd({ "FocusGained", "TermClose", "TermLeave" }, { group = augroup("checktime"), command = "checktime", }) -- Highlight on yank vim.api.nvim_create_autocmd("TextYankPost", { group = augroup("highlight_yank"), callback = function() vim.highlight.on_yank() end, }) -- Resize splits if window got resized vim.api.nvim_create_autocmd({ "VimResized" }, { group = augroup("resize_splits"), callback = function() local current_tab = vim.fn.tabpagenr() vim.cmd("tabdo wincmd =") vim.cmd("tabnext " .. current_tab) end, }) -- Go to last loc when opening a buffer vim.api.nvim_create_autocmd("BufReadPost", { group = augroup("last_loc"), callback = function(event) local exclude = { "gitcommit" } local buf = event.buf if vim.tbl_contains(exclude, vim.bo[buf].filetype) or vim.b[buf].lazyvim_last_loc then return end vim.b[buf].lazyvim_last_loc = true local mark = vim.api.nvim_buf_get_mark(buf, '"') local lcount = vim.api.nvim_buf_line_count(buf) if mark[1] > 0 and mark[1] <= lcount then pcall(vim.api.nvim_win_set_cursor, 0, mark) end end, }) -- Close some filetypes with vim.api.nvim_create_autocmd("FileType", { group = augroup("close_with_q"), pattern = { "PlenaryTestPopup", "help", "lspinfo", "man", "notify", "qf", "query", "spectre_panel", "startuptime", "tsplayground", "neotest-output", "checkhealth", "neotest-summary", "neotest-output-panel", }, callback = function(event) vim.bo[event.buf].buflisted = false vim.keymap.set("n", "q", "close", { buffer = event.buf, silent = true }) end, }) -- Wrap and check for spell in text filetypes vim.api.nvim_create_autocmd("FileType", { group = augroup("wrap_spell"), pattern = { "gitcommit", "markdown" }, callback = function() vim.opt_local.wrap = true vim.opt_local.spell = true end, }) -- Auto create dir when saving a file, in case some intermediate directory does not exist vim.api.nvim_create_autocmd({ "BufWritePre" }, { group = augroup("auto_create_dir"), callback = function(event) if event.match:match("^%w%w+://") then return end local file = vim.loop.fs_realpath(event.match) or event.match vim.fn.mkdir(vim.fn.fnamemodify(file, ":p:h"), "p") end, }) -- Format on save for web files vim.api.nvim_create_autocmd("BufWritePre", { group = augroup("format_web_files"), pattern = { "*.html", "*.css", "*.js", "*.ts", "*.json" }, callback = function() if vim.g.disable_autoformat or vim.b.disable_autoformat then return end local conform = require("conform") conform.format({ async = false, lsp_fallback = true }) end, })