return { -- Color scheme (Catppuccin Mocha) { "catppuccin/nvim", name = "catppuccin", priority = 1000, config = function() require("catppuccin").setup({ flavour = "mocha", background = { light = "latte", dark = "mocha", }, transparent_background = false, show_end_of_buffer = false, term_colors = true, dim_inactive = { enabled = false, shade = "dark", percentage = 0.15, }, styles = { comments = { "italic" }, conditionals = { "italic" }, loops = {}, functions = {}, keywords = { "italic" }, strings = {}, variables = {}, numbers = {}, booleans = {}, properties = {}, types = {}, operators = {}, }, integrations = { cmp = true, gitsigns = true, nvimtree = true, telescope = { enabled = true }, treesitter = true, which_key = true, alpha = true, mason = true, noice = true, notify = true, dap = true, dap_ui = true, native_lsp = { enabled = true, underlines = { errors = { "undercurl" }, hints = { "undercurl" }, warnings = { "undercurl" }, information = { "undercurl" }, }, }, }, }) vim.cmd.colorscheme("catppuccin") end, }, -- File explorer { "nvim-tree/nvim-tree.lua", dependencies = { "nvim-tree/nvim-web-devicons" }, config = function() require("nvim-tree").setup() end, }, -- Toggleterm (integrated terminal) { "akinsho/toggleterm.nvim", version = "*", config = function() require("toggleterm").setup({ size = function(term) if term.direction == "horizontal" then return 15 elseif term.direction == "vertical" then return vim.o.columns * 0.4 end end, open_mapping = [[]], hide_numbers = true, shade_terminals = true, shading_factor = 2, start_in_insert = true, insert_mappings = true, terminal_mappings = true, persist_size = true, persist_mode = true, direction = "float", close_on_exit = true, shell = vim.o.shell, auto_scroll = true, float_opts = { border = "curved", winblend = 0, }, }) local Terminal = require("toggleterm.terminal").Terminal -- Lazygit terminal local lazygit = Terminal:new({ cmd = "lazygit", dir = "git_dir", direction = "float", float_opts = { border = "double" }, on_open = function(term) vim.cmd("startinsert!") end, }) function _G._LAZYGIT_TOGGLE() lazygit:toggle() end -- Python REPL local python = Terminal:new({ cmd = "python3", direction = "horizontal", }) function _G._PYTHON_TOGGLE() python:toggle() end -- Node REPL local node = Terminal:new({ cmd = "node", direction = "horizontal", }) function _G._NODE_TOGGLE() node:toggle() end end, }, -- Session management { "rmagatti/auto-session", config = function() require("auto-session").setup({ log_level = "error", auto_session_suppress_dirs = { "~/", "~/Projects", "~/Downloads", "/" }, auto_session_use_git_branch = true, auto_save_enabled = true, auto_restore_enabled = true, auto_session_root_dir = vim.fn.stdpath("data") .. "/sessions/", }) end, }, -- Notifications { "rcarriga/nvim-notify", config = function() require("notify").setup({ background_colour = "#000000", fps = 60, render = "default", stages = "fade_in_slide_out", timeout = 3000, top_down = true, }) vim.notify = require("notify") end, }, -- Noice (enhanced UI) { "folke/noice.nvim", event = "VeryLazy", dependencies = { "MunifTanjim/nui.nvim", "rcarriga/nvim-notify", }, config = function() require("noice").setup({ lsp = { override = { ["vim.lsp.util.convert_input_to_markdown_lines"] = true, ["vim.lsp.util.stylize_markdown"] = true, ["cmp.entry.get_documentation"] = true, }, hover = { enabled = true }, signature = { enabled = true }, }, presets = { bottom_search = true, command_palette = true, long_message_to_split = true, inc_rename = false, lsp_doc_border = true, }, views = { cmdline_popup = { position = { row = 5, col = "50%" }, size = { width = 60, height = "auto" }, }, }, routes = { { filter = { event = "msg_show", kind = "", find = "written" }, opts = { skip = true }, }, }, }) end, }, -- Git signs { "lewis6991/gitsigns.nvim", event = { "BufReadPre", "BufNewFile" }, config = function() require("gitsigns").setup({ on_attach = function(bufnr) local gs = package.loaded.gitsigns local map = function(mode, l, r, opts) opts = opts or {} opts.buffer = bufnr vim.keymap.set(mode, l, r, opts) end -- Navigation map("n", "]c", function() gs.next_hunk() end, { desc = "Next hunk" }) map("n", "[c", function() gs.prev_hunk() end, { desc = "Previous hunk" }) -- Actions map("n", "gs", gs.stage_hunk, { desc = "Stage hunk" }) map("n", "gr", gs.reset_hunk, { desc = "Reset hunk" }) map("n", "gS", gs.stage_buffer, { desc = "Stage buffer" }) map("n", "gu", gs.undo_stage_hunk, { desc = "Undo stage" }) map("n", "gp", gs.preview_hunk, { desc = "Preview hunk" }) map("n", "gb", function() gs.blame_line({ full = true }) end, { desc = "Blame line" }) map("n", "gd", gs.diffthis, { desc = "Diff this" }) end, }) end, }, -- Diff view { "sindrets/diffview.nvim", cmd = { "DiffviewOpen", "DiffviewFileHistory" }, keys = { { "gv", "DiffviewOpen", desc = "Diff view" }, { "gh", "DiffviewFileHistory %", desc = "File history" }, { "gx", "DiffviewClose", desc = "Close diff" }, }, config = true, }, -- Fuzzy finder { "nvim-telescope/telescope.nvim", dependencies = { "nvim-lua/plenary.nvim" }, config = function() require("telescope").setup() end, }, -- Formatter { "stevearc/conform.nvim", opts = { format_on_save = { timeout_ms = 1000, lsp_fallback = true }, formatters_by_ft = { -- Web javascript = { "prettierd", "prettier" }, javascriptreact = { "prettierd", "prettier" }, typescript = { "prettierd", "prettier" }, typescriptreact = { "prettierd", "prettier" }, json = { "prettierd", "prettier" }, css = { "prettierd", "prettier" }, html = { "prettierd", "prettier" }, markdown = { "prettierd", "prettier" }, -- Rust rust = { "rustfmt" }, -- Python python = { "ruff_format" }, -- C/C++ c = { "clang_format" }, cpp = { "clang_format" }, -- Lua lua = { "stylua" }, }, }, }, -- Syntax highlighting { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate", config = function() -- Install parsers local ensure_installed = { "lua", "vim", "vimdoc", "bash", "javascript", "typescript", "tsx", "json", "yaml", "html", "css", "graphql", "rust", "toml", "python", "c", "cpp", "markdown", "markdown_inline", "sql", } -- Auto-install missing parsers vim.api.nvim_create_autocmd("FileType", { callback = function() local ft = vim.bo.filetype local lang = vim.treesitter.language.get_lang(ft) or ft if vim.tbl_contains(ensure_installed, lang) then pcall(vim.treesitter.start) end end, }) -- Enable treesitter highlighting globally vim.treesitter.start = vim.treesitter.start or function() end -- Run TSUpdate to ensure parsers are installed vim.defer_fn(function() for _, lang in ipairs(ensure_installed) do pcall(function() if not pcall(vim.treesitter.language.inspect, lang) then vim.cmd("TSInstall " .. lang) end end) end end, 100) end, }, -- Autocompletion { "hrsh7th/nvim-cmp", dependencies = { "hrsh7th/cmp-buffer", "hrsh7th/cmp-path", "hrsh7th/cmp-nvim-lsp", "L3MON4D3/LuaSnip", "saadparwaiz1/cmp_luasnip", "rafamadriz/friendly-snippets", }, config = function() local cmp = require("cmp") local luasnip = require("luasnip") require("luasnip.loaders.from_vscode").lazy_load() cmp.setup({ snippet = { expand = function(args) luasnip.lsp_expand(args.body) end }, mapping = cmp.mapping.preset.insert({ [""] = cmp.mapping.complete(), [""] = cmp.mapping.confirm({ select = true }), [""] = cmp.mapping.select_next_item(), [""] = cmp.mapping.select_prev_item(), }), sources = { { name = "nvim_lsp" }, { name = "path" }, { name = "buffer" }, { name = "luasnip" }, { name = "crates" }, }, }) -- SQL files completion vim.api.nvim_create_autocmd("FileType", { pattern = { "sql", "mysql", "plsql" }, callback = function() cmp.setup.buffer({ sources = { { name = "vim-dadbod-completion" }, { name = "buffer" }, }, }) end, }) end, }, -- LSP { "williamboman/mason.nvim", build = ":MasonUpdate", config = true, }, { "williamboman/mason-lspconfig.nvim", dependencies = { "williamboman/mason.nvim" }, config = function() require("mason-lspconfig").setup({ ensure_installed = { -- Web "ts_ls", "eslint", "jsonls", "html", "cssls", "tailwindcss", -- Rust "rust_analyzer", -- Python "pyright", "ruff", -- C/C++ "clangd", }, automatic_installation = true, }) end, }, { "neovim/nvim-lspconfig", dependencies = { "williamboman/mason.nvim", "williamboman/mason-lspconfig.nvim", "hrsh7th/cmp-nvim-lsp", }, config = function() local capabilities = require("cmp_nvim_lsp").default_capabilities() -- LSP keymaps on attach vim.api.nvim_create_autocmd("LspAttach", { callback = function(args) local opts = { buffer = args.buf, silent = true } vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts) vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts) vim.keymap.set("n", "K", vim.lsp.buf.hover, opts) vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts) vim.keymap.set("n", "", vim.lsp.buf.signature_help, opts) vim.keymap.set("n", "rn", vim.lsp.buf.rename, opts) vim.keymap.set("n", "ca", vim.lsp.buf.code_action, opts) vim.keymap.set("n", "gr", vim.lsp.buf.references, opts) vim.keymap.set("n", "lf", function() vim.lsp.buf.format({ async = true }) end, opts) end, }) -- Rust vim.lsp.config("rust_analyzer", { capabilities = capabilities, settings = { ["rust-analyzer"] = { cargo = { allFeatures = true }, checkOnSave = { command = "clippy" }, inlayHints = { bindingModeHints = { enable = true }, chainingHints = { enable = true }, closingBraceHints = { enable = true }, closureReturnTypeHints = { enable = "always" }, lifetimeElisionHints = { enable = "always" }, parameterHints = { enable = true }, typeHints = { enable = true }, }, }, }, }) -- Python (Pyright) vim.lsp.config("pyright", { capabilities = capabilities, settings = { python = { analysis = { autoSearchPaths = true, diagnosticMode = "workspace", useLibraryCodeForTypes = true, typeCheckingMode = "basic", }, }, }, }) -- Python (Ruff for linting) vim.lsp.config("ruff", { capabilities = capabilities, on_attach = function(client, bufnr) client.server_capabilities.hoverProvider = false end, }) -- C/C++ vim.lsp.config("clangd", { capabilities = capabilities, cmd = { "clangd", "--background-index", "--clang-tidy", "--header-insertion=iwyu", "--completion-style=detailed", "--function-arg-placeholders", }, }) -- JSON vim.lsp.config("jsonls", { capabilities = capabilities }) -- HTML vim.lsp.config("html", { capabilities = capabilities }) -- CSS vim.lsp.config("cssls", { capabilities = capabilities }) -- Tailwind vim.lsp.config("tailwindcss", { capabilities = capabilities }) -- ESLint vim.lsp.config("eslint", { capabilities = capabilities }) -- Enable all configured servers vim.lsp.enable({ "rust_analyzer", "pyright", "ruff", "clangd", "jsonls", "html", "cssls", "tailwindcss", "eslint", }) end, }, { "pmizio/typescript-tools.nvim", dependencies = { "nvim-lua/plenary.nvim", "neovim/nvim-lspconfig" }, config = function() require("typescript-tools").setup({ settings = { tsserver_file_preferences = { includeInlayParameterNameHints = "all", includeCompletionsForModuleExports = true }, }, }) end, }, -- Crates.nvim (Rust Cargo.toml) { "saecki/crates.nvim", event = { "BufRead Cargo.toml" }, dependencies = { "nvim-lua/plenary.nvim" }, config = function() require("crates").setup({ smart_insert = true, insert_closing_quote = true, autoload = true, autoupdate = true, loading_indicator = true, popup = { autofocus = false, hide_on_select = false, copy_register = '"', style = "minimal", border = "rounded", show_version_date = true, max_height = 30, min_width = 20, }, completion = { cmp = { enabled = true }, }, }) -- Crates keymaps vim.api.nvim_create_autocmd("BufRead", { pattern = "Cargo.toml", callback = function() local crates = require("crates") vim.keymap.set("n", "ct", crates.toggle, { buffer = true, desc = "Toggle crates" }) vim.keymap.set("n", "cr", crates.reload, { buffer = true, desc = "Reload crates" }) vim.keymap.set("n", "cv", crates.show_versions_popup, { buffer = true, desc = "Show versions" }) vim.keymap.set("n", "cf", crates.show_features_popup, { buffer = true, desc = "Show features" }) vim.keymap.set("n", "cd", crates.show_dependencies_popup, { buffer = true, desc = "Show dependencies" }) vim.keymap.set("n", "cu", crates.update_crate, { buffer = true, desc = "Update crate" }) vim.keymap.set("n", "cU", crates.upgrade_crate, { buffer = true, desc = "Upgrade crate" }) end, }) end, }, -- DAP (debugging) { "mfussenegger/nvim-dap", dependencies = { "rcarriga/nvim-dap-ui", "nvim-neotest/nvim-nio", "theHamsta/nvim-dap-virtual-text", }, config = function() local dap = require("dap") local dapui = require("dapui") dapui.setup({ icons = { expanded = "", collapsed = "", current_frame = "" }, mappings = { expand = { "", "<2-LeftMouse>" }, open = "o", remove = "d", edit = "e", repl = "r", toggle = "t", }, layouts = { { elements = { { id = "scopes", size = 0.25 }, "breakpoints", "stacks", "watches", }, size = 40, position = "left", }, { elements = { "repl", "console" }, size = 0.25, position = "bottom", }, }, }) require("nvim-dap-virtual-text").setup({ enabled = true, enabled_commands = true, highlight_changed_variables = true, highlight_new_as_changed = false, show_stop_reason = true, commented = false, }) 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 vim.fn.sign_define("DapBreakpoint", { text = "", texthl = "DiagnosticSignError" }) vim.fn.sign_define("DapBreakpointCondition", { text = "", texthl = "DiagnosticSignWarn" }) vim.fn.sign_define("DapLogPoint", { text = "", texthl = "DiagnosticSignInfo" }) vim.fn.sign_define("DapStopped", { text = "", texthl = "DiagnosticSignHint", linehl = "Visual" }) vim.fn.sign_define("DapBreakpointRejected", { text = "", texthl = "DiagnosticSignError" }) end, }, -- Python DAP { "mfussenegger/nvim-dap-python", dependencies = { "mfussenegger/nvim-dap" }, ft = "python", config = function() require("dap-python").setup("python3") end, }, -- JS/TS DAP { "mxsdev/nvim-dap-vscode-js", dependencies = { "mfussenegger/nvim-dap", { "microsoft/vscode-js-debug", build = "npm install --legacy-peer-deps && npx gulp vsDebugServerBundle && mv dist out", }, }, config = function() require("dap-vscode-js").setup({ debugger_path = vim.fn.stdpath("data") .. "/lazy/vscode-js-debug", adapters = { "pwa-node", "pwa-chrome", "pwa-msedge", "node-terminal", "pwa-extensionHost" }, }) for _, language in ipairs({ "typescript", "javascript", "typescriptreact", "javascriptreact" }) do require("dap").configurations[language] = { { type = "pwa-node", request = "launch", name = "Launch file", program = "${file}", cwd = "${workspaceFolder}", }, { type = "pwa-node", request = "attach", name = "Attach", processId = require("dap.utils").pick_process, cwd = "${workspaceFolder}", }, { type = "pwa-chrome", request = "launch", name = "Launch Chrome", url = "http://localhost:3000", webRoot = "${workspaceFolder}", }, } end end, }, -- Markdown preview { "iamcco/markdown-preview.nvim", cmd = { "MarkdownPreviewToggle", "MarkdownPreview", "MarkdownPreviewStop" }, ft = { "markdown" }, build = function() vim.fn["mkdp#util#install"]() end, config = function() vim.g.mkdp_auto_start = 0 vim.g.mkdp_auto_close = 1 vim.g.mkdp_refresh_slow = 0 vim.g.mkdp_browser = "" vim.g.mkdp_preview_options = { mkit = {}, katex = {}, uml = {}, maid = {}, disable_sync_scroll = 0, sync_scroll_type = "middle", hide_yaml_meta = 1, } end, }, -- Database client { "tpope/vim-dadbod", cmd = { "DB", "DBUI", "DBUIToggle", "DBUIAddConnection" }, }, { "kristijanhusak/vim-dadbod-ui", dependencies = { { "tpope/vim-dadbod", lazy = true }, { "kristijanhusak/vim-dadbod-completion", ft = { "sql", "mysql", "plsql" }, lazy = true }, }, cmd = { "DBUI", "DBUIToggle", "DBUIAddConnection", "DBUIFindBuffer" }, init = function() vim.g.db_ui_use_nerd_fonts = 1 vim.g.db_ui_save_location = vim.fn.stdpath("data") .. "/db_ui" end, }, -- HTTP client { "rest-nvim/rest.nvim", dependencies = { "nvim-lua/plenary.nvim" }, ft = "http", config = function() require("rest-nvim").setup({ result_split_horizontal = false, result_split_in_place = false, skip_ssl_verification = false, encode_url = true, highlight = { enabled = true, timeout = 150, }, result = { show_url = true, show_curl_command = false, show_http_info = true, show_headers = true, formatters = { json = "jq", }, }, jump_to_request = false, env_file = ".env", yank_dry_run = true, }) end, }, -- Colorizer { "NvChad/nvim-colorizer.lua", opts = { user_default_options = { names = false } } }, -- Status line { "nvim-lualine/lualine.nvim", dependencies = { "nvim-tree/nvim-web-devicons" }, config = function() require("lualine").setup({ options = { theme = "catppuccin" }, }) end, }, -- Which-key { "folke/which-key.nvim", event = "VeryLazy", config = function() local wk = require("which-key") wk.setup({ preset = "modern", }) wk.add({ { "e", desc = "Toggle file explorer" }, { "f", group = "Find" }, { "ff", desc = "Find files" }, { "fg", desc = "Live grep" }, { "fb", desc = "Find buffers" }, { "g", group = "Git" }, { "w", desc = "Save" }, { "q", desc = "Quit" }, { "b", group = "Buffers" }, { "t", group = "Terminal" }, { "d", group = "Debug" }, { "l", group = "LSP" }, { "c", group = "Crates (Rust)" }, { "s", group = "Session" }, { "n", group = "Notifications" }, { "h", group = "HTTP" }, { "D", group = "Database" }, { "m", group = "Markdown" }, }) end, }, -- Alpha (dashboard) { "goolord/alpha-nvim", config = function() local alpha = require('alpha') local dashboard = require("alpha.themes.dashboard") dashboard.section.header.val = { [[ ^ ^ ^ ^☆ ★ ☆ ___I_☆ ★ ☆ ^ ^ ^ ^ ^ ^ ^ ]], [[ /|\/|\/|\ /|\ ★☆ /\-_--\ ☆ ★/|\/|\ /|\/|\/|\ /|\/|\ ]], [[ /|\/|\/|\ /|\ ★ / \_-__\☆ ★/|\/|\ /|\/|\/|\ /|\/|\ ]], [[ /|\/|\/|\ /|\ 󰻀 |[]| [] | 󰻀 /|\/|\ /|\/|\/|\ /|\/|\ ]], } dashboard.section.buttons.val = { dashboard.button("e", " New file", ":ene startinsert "), dashboard.button("f", "󰍉 Find file", ":Telescope find_files"), dashboard.button("r", " Recent files", ":Telescope oldfiles"), dashboard.button("t", " Browse cwd", ":NvimTreeOpen"), dashboard.button("s", " Restore session", ":SessionRestore"), dashboard.button("g", "󰊢 Lazygit", ":lua _LAZYGIT_TOGGLE()"), dashboard.button("c", " Config", ":e ~/.config/nvim/"), dashboard.button("p", " Plugins", ":Lazy"), dashboard.button("q", "󰅙 Quit", ":qa"), } dashboard.section.footer.val = function() return vim.g.startup_time_ms or "[[ ]]" end dashboard.section.buttons.opts.hl = "Keyword" dashboard.opts.opts.noautocmd = true alpha.setup(dashboard.opts) end, }, }