-- ============================================================================= -- init.lua — Full IDE Neovim Config (Rust, Go, C++, Python, Writing) -- Bootstrap: place at ~/.config/nvim/init.lua -- ============================================================================= -- ----------------------------------------------------------------------------- -- BOOTSTRAP lazy.nvim -- ----------------------------------------------------------------------------- local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not vim.loop.fs_stat(lazypath) then vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", "--branch=stable", lazypath, }) end vim.opt.rtp:prepend(lazypath) -- ----------------------------------------------------------------------------- -- CORE OPTIONS -- ----------------------------------------------------------------------------- vim.g.mapleader = " " vim.g.maplocalleader = " " local opt = vim.opt opt.number = true opt.relativenumber = true opt.cursorline = true opt.signcolumn = "yes" opt.scrolloff = 8 opt.sidescrolloff = 8 opt.tabstop = 4 opt.shiftwidth = 4 opt.expandtab = true opt.smartindent = true opt.wrap = false opt.linebreak = true -- soft-wrap for prose (toggled per filetype) opt.ignorecase = true opt.smartcase = true opt.hlsearch = false opt.incsearch = true opt.splitright = true opt.splitbelow = true opt.termguicolors = true opt.pumheight = 10 opt.updatetime = 200 opt.timeoutlen = 300 opt.undofile = true -- persistent undo opt.swapfile = false opt.backup = false opt.completeopt = { "menu", "menuone", "noselect" } opt.fileencoding = "utf-8" opt.clipboard = "unnamedplus" -- Highlight yanked text briefly vim.api.nvim_create_autocmd("TextYankPost", { callback = function() vim.highlight.on_yank({ timeout = 200 }) end, }) -- Prose mode for markdown/text files vim.api.nvim_create_autocmd("FileType", { pattern = { "markdown", "text", "gitcommit" }, callback = function() opt.wrap = true opt.spell = true opt.spelllang = "en_us" end, }) -- ----------------------------------------------------------------------------- -- PLUGINS via lazy.nvim -- ----------------------------------------------------------------------------- require("lazy").setup({ -- ── Colorscheme ───────────────────────────────────────────────────────────── { "catppuccin/nvim", name = "catppuccin", priority = 1000, config = function() require("catppuccin").setup({ flavour = "mocha", integrations = { nvimtree = true, telescope = true, cmp = true, gitsigns = true, }}) vim.cmd.colorscheme("catppuccin") end, }, -- ── Icons ──────────────────────────────────────────────────────────────────── { "nvim-tree/nvim-web-devicons", lazy = true }, -- ── Status Line ───────────────────────────────────────────────────────────── { "nvim-lualine/lualine.nvim", event = "VeryLazy", config = function() require("lualine").setup({ options = { theme = "catppuccin", globalstatus = true }, sections = { lualine_c = { { "filename", path = 1 } }, lualine_x = { "diagnostics", "encoding", "filetype" }, }, }) end, }, -- ── File Tree ─────────────────────────────────────────────────────────────── { "nvim-tree/nvim-tree.lua", keys = { { "e", "NvimTreeToggle", desc = "File Tree" } }, config = function() require("nvim-tree").setup({ view = { width = 35 }, renderer = { group_empty = true }, filters = { dotfiles = false }, git = { enable = true }, actions = { open_file = { quit_on_open = true } }, }) end, }, -- ── Fuzzy Finder ──────────────────────────────────────────────────────────── { "nvim-telescope/telescope.nvim", branch = "0.1.x", dependencies = { "nvim-lua/plenary.nvim", { "nvim-telescope/telescope-fzf-native.nvim", build = "make" }, }, keys = { { "ff", "Telescope find_files", desc = "Find Files" }, { "fg", "Telescope live_grep", desc = "Live Grep" }, { "fb", "Telescope buffers", desc = "Buffers" }, { "fh", "Telescope help_tags", desc = "Help" }, { "fd", "Telescope diagnostics", desc = "Diagnostics" }, { "fr", "Telescope lsp_references", desc = "References" }, }, config = function() local ts = require("telescope") ts.setup({ defaults = { path_display = { "smart" }, layout_strategy = "horizontal" } }) ts.load_extension("fzf") end, }, -- ── Treesitter ─────────────────────────────────────────────────────────────── { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate", event = { "BufReadPost", "BufNewFile" }, dependencies = { "nvim-treesitter/nvim-treesitter-textobjects" }, config = function() require("nvim-treesitter.configs").setup({ ensure_installed = { "lua", "vim", "vimdoc", "rust", "go", "cpp", "c", "python", "markdown", "markdown_inline", "json", "yaml", "toml", "bash", }, highlight = { enable = true }, indent = { enable = true }, textobjects = { select = { enable = true, lookahead = true, keymaps = { ["af"] = "@function.outer", ["if"] = "@function.inner", ["ac"] = "@class.outer", ["ic"] = "@class.inner", }, }, move = { enable = true, goto_next_start = { ["]f"] = "@function.outer", ["]c"] = "@class.outer" }, goto_previous_start = { ["[f"] = "@function.outer", ["[c"] = "@class.outer" }, }, }, }) end, }, -- ── LSP ────────────────────────────────────────────────────────────────────── { "neovim/nvim-lspconfig", dependencies = { "williamboman/mason.nvim", "williamboman/mason-lspconfig.nvim", "hrsh7th/cmp-nvim-lsp", { "j-hui/fidget.nvim", opts = {} }, -- LSP progress UI }, event = { "BufReadPre", "BufNewFile" }, config = function() -- Mason: auto-install LSP servers require("mason").setup({ ui = { border = "rounded" } }) require("mason-lspconfig").setup({ ensure_installed = { "rust_analyzer", "gopls", "clangd", "pyright", "lua_ls", }, automatic_installation = true, }) local lspconfig = require("lspconfig") local caps = require("cmp_nvim_lsp").default_capabilities() -- Shared on_attach: keymaps available once LSP attaches local on_attach = function(_, buf) local map = function(k, f, d) vim.keymap.set("n", k, f, { buffer = buf, desc = d }) end map("gd", vim.lsp.buf.definition, "Go to Definition") map("gD", vim.lsp.buf.declaration, "Go to Declaration") map("gi", vim.lsp.buf.implementation, "Go to Implementation") map("gt", vim.lsp.buf.type_definition, "Type Definition") map("K", vim.lsp.buf.hover, "Hover Docs") map("rn", vim.lsp.buf.rename, "Rename Symbol") map("ca", vim.lsp.buf.code_action, "Code Action") map("cf", function() vim.lsp.buf.format({ async = true }) end, "Format") map("[d", vim.diagnostic.goto_prev, "Prev Diagnostic") map("]d", vim.diagnostic.goto_next, "Next Diagnostic") map("dl", vim.diagnostic.open_float, "Line Diagnostics") end -- Per-server setup local servers = { rust_analyzer = { settings = { ["rust-analyzer"] = { checkOnSave = { command = "clippy" }, inlayHints = { enable = true }, }}, }, gopls = { settings = { gopls = { analyses = { unusedparams = true }, staticcheck = true, gofumpt = true, }}, }, clangd = { cmd = { "clangd", "--background-index", "--clang-tidy", "--header-insertion=iwyu", "--completion-style=detailed" }, }, pyright = { settings = { python = { analysis = { typeCheckingMode = "basic", autoImportCompletions = true, }}}, }, lua_ls = { settings = { Lua = { runtime = { version = "LuaJIT" }, workspace = { checkThirdParty = false }, diagnostics = { globals = { "vim" } }, telemetry = { enable = false }, }}, }, } for server, cfg in pairs(servers) do cfg.on_attach = on_attach cfg.capabilities = caps lspconfig[server].setup(cfg) end -- 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 vim.diagnostic.config({ virtual_text = { prefix = "●" }, update_in_insert = false, severity_sort = true, float = { border = "rounded" }, }) end, }, -- ── Autocompletion ─────────────────────────────────────────────────────────── { "hrsh7th/nvim-cmp", event = "InsertEnter", dependencies = { "hrsh7th/cmp-nvim-lsp", "hrsh7th/cmp-buffer", "hrsh7th/cmp-path", "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 }, window = { completion = cmp.config.window.bordered(), documentation = cmp.config.window.bordered(), }, mapping = cmp.mapping.preset.insert({ [""] = cmp.mapping.select_prev_item(), [""] = cmp.mapping.select_next_item(), [""] = cmp.mapping.scroll_docs(-4), [""] = cmp.mapping.scroll_docs(4), [""] = cmp.mapping.complete(), [""] = cmp.mapping.abort(), [""] = cmp.mapping.confirm({ select = false }), [""] = 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", priority = 1000 }, { name = "luasnip", priority = 750 }, { name = "buffer", priority = 500 }, { name = "path", priority = 250 }, }), formatting = { format = function(entry, item) local icons = { Text = "󰉿", Method = "󰆧", Function = "󰊕", Constructor = "", Field = "󰜢", Variable = "󰀫", Class = "󰠱", Interface = "", Module = "", Property = "󰜢", Unit = "󰑭", Value = "󰎠", Enum = "", Keyword = "󰌋", Snippet = "", Color = "󰏘", File = "󰈙", Reference = "󰈇", Folder = "󰉋", EnumMember = "", Constant = "󰏿", Struct = "󰙅", Event = "", Operator = "󰆕", TypeParameter = "", } item.kind = string.format("%s %s", icons[item.kind] or "", item.kind) item.menu = ({ nvim_lsp = "[LSP]", luasnip = "[Snip]", buffer = "[Buf]", path = "[Path]" })[entry.source.name] return item end, }, }) end, }, -- ── Formatting ─────────────────────────────────────────────────────────────── { "stevearc/conform.nvim", event = "BufWritePre", keys = { { "cf", function() require("conform").format({ async = true }) end, desc = "Format" }, }, opts = { formatters_by_ft = { rust = { "rustfmt" }, go = { "gofmt", "goimports" }, cpp = { "clang_format" }, c = { "clang_format" }, python = { "black", "isort" }, lua = { "stylua" }, }, format_on_save = { timeout_ms = 500, lsp_fallback = true }, }, }, -- ── Git ────────────────────────────────────────────────────────────────────── { "lewis6991/gitsigns.nvim", event = { "BufReadPre", "BufNewFile" }, opts = { signs = { add = { text = "▎" }, change = { text = "▎" }, delete = { text = "" }, topdelete = { text = "" }, changedelete = { text = "▎" }, }, on_attach = function(buf) local gs = require("gitsigns") local map = function(k, f, d) vim.keymap.set("n", k, f, { buffer = buf, desc = d }) end map("]h", gs.next_hunk, "Next Hunk") map("[h", gs.prev_hunk, "Prev Hunk") map("hs", gs.stage_hunk, "Stage Hunk") map("hr", gs.reset_hunk, "Reset Hunk") map("hb", gs.blame_line, "Blame Line") map("hd", gs.diffthis, "Diff This") map("hp", gs.preview_hunk, "Preview Hunk") end, }, }, -- ── Buffers / Tabs ─────────────────────────────────────────────────────────── { "akinsho/bufferline.nvim", event = "VeryLazy", keys = { { "", "BufferLineCyclePrev", desc = "Prev Buffer" }, { "", "BufferLineCycleNext", desc = "Next Buffer" }, { "bd", "bdelete", desc = "Delete Buffer" }, }, opts = { options = { diagnostics = "nvim_lsp", offsets = {{ filetype = "NvimTree", text = "Files", padding = 1 }}, show_buffer_close_icons = true, }, }, }, -- ── Terminal ───────────────────────────────────────────────────────────────── { "akinsho/toggleterm.nvim", keys = { { "", "ToggleTerm", desc = "Toggle Terminal" } }, opts = { direction = "horizontal", size = 15, shade_terminals = true }, }, -- ── Pairs & Surround ───────────────────────────────────────────────────────── { "windwp/nvim-autopairs", event = "InsertEnter", config = function() local ap = require("nvim-autopairs") local cmp = require("cmp") ap.setup({ check_ts = true }) cmp.event:on("confirm_done", require("nvim-autopairs.completion.cmp").on_confirm_done()) end, }, { "kylechui/nvim-surround", event = "VeryLazy", version = "*", opts = {}, }, -- ── Comments ───────────────────────────────────────────────────────────────── { "numToStr/Comment.nvim", event = "VeryLazy", opts = {}, }, -- ── Which-key (keymap helper) ───────────────────────────────────────────────── { "folke/which-key.nvim", event = "VeryLazy", config = function() require("which-key").setup() require("which-key").register({ ["f"] = { name = "Find" }, ["c"] = { name = "Code" }, ["h"] = { name = "Git Hunks" }, ["b"] = { name = "Buffer" }, ["d"] = { name = "Diagnostics" }, }) end, }, -- ── Indent guides ───────────────────────────────────────────────────────────── { "lukas-reineke/indent-blankline.nvim", event = { "BufReadPost", "BufNewFile" }, main = "ibl", opts = { indent = { char = "│" }, scope = { enabled = true } }, }, -- ── Rust extras ────────────────────────────────────────────────────────────── { "mrcjkb/rustaceanvim", ft = "rust", version = "^4", }, -- ── Go extras ──────────────────────────────────────────────────────────────── { "ray-x/go.nvim", dependencies = { "ray-x/guihua.lua", "neovim/nvim-lspconfig", "nvim-treesitter/nvim-treesitter" }, ft = { "go", "gomod" }, build = ":GoInstallBinaries", opts = { lsp_inlay_hints = { enable = true } }, }, -- ── Markdown preview ───────────────────────────────────────────────────────── { "MeanderingProgrammer/render-markdown.nvim", ft = "markdown", dependencies = { "nvim-treesitter/nvim-treesitter", "nvim-tree/nvim-web-devicons" }, opts = {}, }, -- ── Trouble (diagnostics panel) ────────────────────────────────────────────── { "folke/trouble.nvim", keys = { { "xx", "TroubleToggle", desc = "Toggle Trouble" }, { "xw", "TroubleToggle workspace_diagnostics", desc = "Workspace Diagnostics" }, { "xd", "TroubleToggle document_diagnostics", desc = "Document Diagnostics" }, }, opts = { use_diagnostic_signs = true }, }, }, { -- lazy.nvim UI options ui = { border = "rounded" }, checker = { enabled = true, notify = false }, }) -- ----------------------------------------------------------------------------- -- EXTRA KEYMAPS -- ----------------------------------------------------------------------------- local map = vim.keymap.set -- Better window navigation map("n", "", "h") map("n", "", "j") map("n", "", "k") map("n", "", "l") -- Resize windows map("n", "", "resize +2") map("n", "", "resize -2") map("n", "", "vertical resize -2") map("n", "", "vertical resize +2") -- Stay in visual mode after indent map("v", "<", "", ">gv") -- Move lines up/down map("v", "J", ":m '>+1gv=gv") map("v", "K", ":m '<-2gv=gv") -- Keep cursor centered when jumping map("n", "", "zz") map("n", "", "zz") map("n", "n", "nzzzv") map("n", "N", "Nzzzv") -- Paste without losing register map("x", "p", '"_dP', { desc = "Paste without losing register" }) -- Save & quit shortcuts map("n", "w", "w", { desc = "Save" }) map("n", "q", "q", { desc = "Quit" }) map("n", "Q", "qa!", { desc = "Quit all" }) -- Clear search highlight map("n", "", "nohlsearch")