--- /dev/null
+vim.g.mapleader = " "
+vim.g.maplocalleader = " "
+
+require("config.options")
+require("config.keymaps")
+require("config.autocmds")
+
+-- load lazy bootstrap here (NOT under lua/plugins/)
+require("config.lazy")
+
+-- colors after plugins
+vim.cmd.colorscheme("catppuccin-mocha")
--- /dev/null
+{
+ "Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" },
+ "LuaSnip": { "branch": "master", "commit": "73813308abc2eaeff2bc0d3f2f79270c491be9d7" },
+ "catppuccin": { "branch": "main", "commit": "af58927c55c9f3272c940ff02b3cee94a1249f26" },
+ "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
+ "cmp-nvim-lsp": { "branch": "main", "commit": "bd5a7d6db125d4654b50eeae9f5217f24bb22fd3" },
+ "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
+ "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
+ "conform.nvim": { "branch": "master", "commit": "fbcb4fa7f34bfea9be702ffff481a8e336ebf6ed" },
+ "friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" },
+ "gitsigns.nvim": { "branch": "main", "commit": "1ee5c1fd068c81f9dd06483e639c2aa4587dc197" },
+ "gruvbox.nvim": { "branch": "main", "commit": "5e0a460d8e0f7f669c158dedd5f9ae2bcac31437" },
+ "harpoon": { "branch": "harpoon2", "commit": "ed1f853847ffd04b2b61c314865665e1dadf22c7" },
+ "indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" },
+ "lazy.nvim": { "branch": "main", "commit": "1ea3c4085785f460fb0e46d2fe1ee895f5f9e7c1" },
+ "lazygit.nvim": { "branch": "main", "commit": "2305deed25bc61b866d5d39189e9105a45cf1cfb" },
+ "lualine.nvim": { "branch": "master", "commit": "b8c23159c0161f4b89196f74ee3a6d02cdc3a955" },
+ "markdown-preview.nvim": { "branch": "master", "commit": "a923f5fc5ba36a3b17e289dc35dc17f66d0548ee" },
+ "mason-lspconfig.nvim": { "branch": "main", "commit": "6bdb14f230de0904229ec367b410fb817e59b072" },
+ "mason.nvim": { "branch": "main", "commit": "ad7146aa61dcaeb54fa900144d768f040090bff0" },
+ "neo-tree.nvim": { "branch": "v3.x", "commit": "4c60a198e3f92098778a32a1c76d2bd7ba46a3b5" },
+ "none-ls.nvim": { "branch": "main", "commit": "5cf63841461b49989972d35bf886e076a1ab3649" },
+ "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
+ "nvim-cmp": { "branch": "main", "commit": "b5311ab3ed9c846b585c0c15b7559be131ec4be9" },
+ "nvim-lspconfig": { "branch": "master", "commit": "ac98db2f9f06a56498ec890a96928774eae412c3" },
+ "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
+ "nvim-web-devicons": { "branch": "master", "commit": "b8221e42cf7287c4dcde81f232f58d7b947c210d" },
+ "oil.nvim": { "branch": "master", "commit": "919e155fdf38e9148cdb5304faaaf53c20d703ea" },
+ "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
+ "render-markdown.nvim": { "branch": "main", "commit": "d53856423be5ef3c267d26ee261b0981b372f718" },
+ "telescope.nvim": { "branch": "master", "commit": "b4da76be54691e854d3e0e02c36b0245f945c2c7" },
+ "todo-comments.nvim": { "branch": "main", "commit": "19d461ddd543e938eb22505fb03fa878800270b6" },
+ "toggleterm.nvim": { "branch": "main", "commit": "50ea089fc548917cc3cc16b46a8211833b9e3c7c" },
+ "trouble.nvim": { "branch": "main", "commit": "c098362fe603d3922095e7db595961e020bdf2d0" },
+ "vim-fugitive": { "branch": "master", "commit": "61b51c09b7c9ce04e821f6cf76ea4f6f903e3cf4" },
+ "vimtex": { "branch": "master", "commit": "5c457a8a4dd57ca942260ff843a5482b064d9d9b" },
+ "which-key.nvim": { "branch": "main", "commit": "b4177e3eaf15fe5eb8357ebac2286d488be1ed00" },
+ "window-picker": { "branch": "main", "commit": "6382540b2ae5de6c793d4aa2e3fe6dbb518505ec" }
+}
--- /dev/null
+-- Format on save (sync, safe fallback to LSP)
+vim.api.nvim_create_autocmd("BufWritePre", {
+ callback = function(args)
+ require("conform").format({ bufnr = args.buf, async = false, lsp_fallback = true })
+ end,
+})
--- /dev/null
+local map = vim.keymap.set
+
+map("n", "<leader>q", "<cmd>q<cr>")
+map("n", "<leader>w", "<cmd>w<cr>")
+
+-- Files / search
+map("n", "<leader>e", "<cmd>Oil<cr>", { desc = "File Explorer (oil)" })
+map("n", "<leader>ff", "<cmd>Telescope find_files<cr>", { desc = "Find files" })
+map("n", "<leader>fg", "<cmd>Telescope live_grep<cr>", { desc = "Grep" })
+map("n", "<leader>fb", "<cmd>Telescope buffers<cr>", { desc = "Buffers" })
+map("n", "<leader>fh", "<cmd>Telescope help_tags<cr>", { desc = "Help" })
+
+-- Git / terminal / diagnostics
+map("n", "<leader>gg", "<cmd>LazyGit<cr>", { desc = "LazyGit" })
+map("n", "<leader>tt", "<cmd>ToggleTerm<cr>", { desc = "Terminal" })
+map("n", "<leader>xx", "<cmd>Trouble diagnostics toggle<cr>", { desc = "Diagnostics (Trouble)" })
+
+-- LSP
+map("n", "<leader>rn", vim.lsp.buf.rename, { desc = "LSP Rename" })
+map({ "n", "v" }, "<leader>ca", vim.lsp.buf.code_action, { desc = "LSP Code Action" })
+map("n", "gd", vim.lsp.buf.definition, { desc = "Go to definition" })
+map("n", "K", vim.lsp.buf.hover, { desc = "Hover" })
+
+-- Format
+map("n", "<leader>lf", function()
+ require("conform").format({ async = true })
+end, { desc = "Format" })
+
+-- QoL
+map("n", "<leader>s", [[:%s/\s\+$//e<cr>]], { desc = "Strip trailing spaces" })
+
+-- Neotree
+
+-- Focus the sidebar (or open it if closed)
+map("n", "<leader>nf", function()
+ require("neo-tree.command").execute({ action = "focus", source = "filesystem", position = "left" })
+end, { desc = "Neo-tree: focus sidebar" })
+
+-- Reveal current file in the sidebar (and focus it)
+map("n", "<leader>fE", "<cmd>Neotree reveal left<cr>", { desc = "Neo-tree: reveal current file" })
--- /dev/null
+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)
+
+require("lazy").setup({
+ { import = "plugins" }, -- load all specs from lua/plugins/*.lua
+}, {
+ ui = { border = "rounded" },
+ change_detection = { notify = false },
+})
--- /dev/null
+local o, wo = vim.o, vim.wo
+o.termguicolors = true
+o.number = true
+wo.signcolumn = "yes"
+o.expandtab = true
+o.shiftwidth = 2
+o.tabstop = 2
+o.ignorecase = true
+o.smartcase = true
+o.clipboard = "unnamedplus"
+o.updatetime = 250
+o.splitbelow = true
+o.splitright = true
--- /dev/null
+return {
+ {
+ "hrsh7th/nvim-cmp",
+ dependencies = {
+ "hrsh7th/cmp-nvim-lsp","hrsh7th/cmp-path","hrsh7th/cmp-buffer",
+ "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({
+ ["<C-Space>"] = cmp.mapping.complete(),
+ ["<CR>"] = cmp.mapping.confirm({ select = true }),
+ ["<Tab>"] = cmp.mapping(function(fb)
+ if cmp.visible() then cmp.select_next_item()
+ elseif luasnip.expand_or_jumpable() then luasnip.expand_or_jump()
+ else fb() end
+ end, { "i","s" }),
+ ["<S-Tab>"] = cmp.mapping(function(fb)
+ if cmp.visible() then cmp.select_prev_item()
+ elseif luasnip.jumpable(-1) then luasnip.jump(-1)
+ else fb() end
+ end, { "i","s" }),
+ }),
+ sources = { {name="nvim_lsp"},{name="path"},{name="buffer"},{name="luasnip"} },
+ })
+ end,
+ },
+}
--- /dev/null
+return {
+ { "numToStr/Comment.nvim", opts = {} },
+}
--- /dev/null
+return {
+ { "stevearc/oil.nvim", opts = {} }, -- file explorer as buffer
+}
--- /dev/null
+return {
+ {
+ "stevearc/conform.nvim",
+ opts = {
+ formatters_by_ft = {
+ python = { "ruff_fix", "ruff_format", "black" },
+ lua = { "stylua" },
+ sh = { "shfmt" },
+ bash = { "shfmt" },
+ markdown = { "prettier", "mdformat" },
+ yaml = { "prettier" },
+ json = { "prettier" },
+ tex = {}, -- optionally "latexindent"
+ },
+ notify_on_error = true,
+ },
+ },
+ {
+ "nvimtools/none-ls.nvim",
+ dependencies = { "nvim-lua/plenary.nvim" },
+ config = function()
+ local nls = require("null-ls")
+ nls.setup({
+ sources = {
+ nls.builtins.diagnostics.shellcheck,
+ nls.builtins.diagnostics.markdownlint,
+ },
+ })
+ end,
+ },
+}
--- /dev/null
+return {
+ { "lewis6991/gitsigns.nvim", opts = {} },
+ { "kdheepak/lazygit.nvim" },
+ { "tpope/vim-fugitive" },
+}
+
--- /dev/null
+return {
+ {
+ "ThePrimeagen/harpoon",
+ branch = "harpoon2",
+ config = function()
+ local harpoon = require("harpoon")
+ harpoon:setup()
+ local map = vim.keymap.set
+ map("n", "<leader>ha", function() harpoon:list():add() end, { desc = "Harpoon add" })
+ map("n", "<leader>hm", function() harpoon.ui:toggle_quick_menu(harpoon:list()) end, { desc = "Harpoon menu" })
+ end,
+ },
+}
--- /dev/null
+return {
+ { "lervag/vimtex", ft = { "tex" } },
+}
--- /dev/null
+return {
+ { "williamboman/mason.nvim", opts = {} },
+
+ {
+ "williamboman/mason-lspconfig.nvim",
+ dependencies = { "williamboman/mason.nvim", "neovim/nvim-lspconfig" },
+ opts = {
+ ensure_installed = {
+ "pyright",
+ "ruff",
+ "bashls",
+ "lua_ls",
+ "texlab",
+ "dockerls",
+ "yamlls",
+ "marksman",
+ },
+ -- v2 can auto-enable; installation still happens via Mason
+ automatic_enable = true,
+ },
+ },
+
+ {
+ "neovim/nvim-lspconfig",
+ dependencies = { "hrsh7th/cmp-nvim-lsp" },
+ config = function()
+ local caps = require("cmp_nvim_lsp").default_capabilities()
+
+ vim.diagnostic.config({
+ virtual_text = { spacing = 2, prefix = "●" },
+ float = { border = "rounded" },
+ severity_sort = true,
+ })
+
+ local function on_attach(_, _) end
+
+ vim.lsp.config("pyright", { capabilities = caps, on_attach = on_attach })
+ vim.lsp.config(
+ "ruff",
+ { capabilities = caps, on_attach = on_attach, init_options = { settings = { args = {} } } }
+ )
+ vim.lsp.config("bashls", { capabilities = caps, on_attach = on_attach })
+ vim.lsp.config("lua_ls", {
+ capabilities = caps,
+ on_attach = on_attach,
+ settings = {
+ Lua = {
+ diagnostics = { globals = { "vim" } },
+ workspace = { checkThirdParty = false },
+ format = { enable = false },
+ },
+ },
+ })
+ vim.lsp.config("texlab", { capabilities = caps, on_attach = on_attach })
+ vim.lsp.config("dockerls", { capabilities = caps, on_attach = on_attach })
+ vim.lsp.config("yamlls", { capabilities = caps, on_attach = on_attach })
+ vim.lsp.config("marksman", { capabilities = caps, on_attach = on_attach })
+ end,
+ },
+}
--- /dev/null
+return {
+ -- Browser preview (unchanged)
+ { "iamcco/markdown-preview.nvim", build = "cd app && npm install", ft = { "markdown" } },
+
+ -- Right-side Glow preview, lazy-loaded for Markdown buffers
+ {
+ "nvim-lua/plenary.nvim", -- just a safe carrier plugin
+ ft = { "markdown" },
+ config = function()
+ -- Key: toggle right-side preview for the current markdown buffer
+ vim.keymap.set("n", "<leader>mp", function()
+ require("config.md_preview").toggle()
+ end, { buffer = true, desc = "Markdown: preview (toggle right split)" })
+
+ -- Command: :MarkdownPane
+ vim.api.nvim_buf_create_user_command(0, "MarkdownPane", function()
+ require("config.md_preview").toggle()
+ end, {})
+
+ -- OPTIONAL auto-open preview for *.md files.
+ -- If you don't want auto-open, delete this block.
+ vim.api.nvim_create_autocmd("BufReadPost", {
+ buffer = 0, -- current markdown buffer only
+ once = true, -- run once per buffer
+ callback = function()
+ -- comment this out if you prefer manual toggle only
+ -- require("config.md_preview").toggle()
+ end,
+ })
+ end,
+ },
+}
--- /dev/null
+return {
+ {
+ "nvim-neo-tree/neo-tree.nvim",
+ branch = "v3.x",
+ dependencies = {
+ "nvim-lua/plenary.nvim",
+ "nvim-tree/nvim-web-devicons", -- you already have this
+ "MunifTanjim/nui.nvim",
+ },
+ cmd = { "Neotree" },
+ keys = {
+ { "<leader>fe", "<cmd>Neotree toggle left<cr>", desc = "Files (toggle sidebar)" },
+ { "<leader>fE", "<cmd>Neotree reveal left<cr>", desc = "Files (reveal current)" },
+ { "<leader>fgs", "<cmd>Neotree float git_status<cr>", desc = "Git status (float)" },
+ },
+ opts = {
+ close_if_last_window = true,
+ popup_border_style = "rounded",
+ enable_git_status = true,
+ enable_diagnostics = false,
+ default_component_configs = {
+ indent = { padding = 1 },
+ name = { use_git_status_colors = true },
+ git_status = {
+ symbols = { unstaged = "●", staged = "✓", untracked = "★", renamed = "➡", deleted = "✖" },
+ },
+ },
+ filesystem = {
+ filtered_items = {
+ hide_dotfiles = false,
+ hide_gitignored = true,
+ },
+ follow_current_file = { enabled = true },
+ hijack_netrw_behavior = "open_default",
+ use_libuv_file_watcher = true,
+ },
+ window = {
+ width = 34,
+ mappings = {
+ ["<space>"] = "none",
+ ["l"] = "open",
+ ["h"] = "close_node",
+ ["o"] = "open",
+ ["<cr>"] = "open_with_window_picker",
+ ["s"] = "open_split",
+ ["v"] = "open_vsplit",
+ ["C"] = "close_all_nodes",
+ ["R"] = "refresh",
+ ["a"] = { "add", config = { show_path = "relative" } },
+ ["d"] = "delete",
+ ["r"] = "rename",
+ ["y"] = "copy_to_clipboard",
+ ["x"] = "cut_to_clipboard",
+ ["p"] = "paste_from_clipboard",
+ },
+ },
+ source_selector = { winbar = true, statusline = false }, -- nice tabs for Files/Git/Buffers
+ },
+ init = function()
+ -- Optional: open the sidebar when you start in a directory or no file
+ if vim.fn.argc() == 0 then
+ vim.api.nvim_create_autocmd("VimEnter", {
+ once = true,
+ callback = function()
+ vim.cmd("Neotree show left")
+ end,
+ })
+ end
+ end,
+ },
+}
--- /dev/null
+return {
+ {
+ "MeanderingProgrammer/render-markdown.nvim",
+ ft = { "markdown" },
+ dependencies = {
+ "nvim-treesitter/nvim-treesitter",
+ "nvim-tree/nvim-web-devicons",
+ },
+ keys = {
+ -- Open a right split showing the SAME buffer, render only in that right window
+ {
+ "<leader>mr",
+ function()
+ local cur = vim.api.nvim_get_current_buf()
+ vim.cmd("vsplit")
+ vim.cmd("wincmd l")
+ vim.api.nvim_win_set_buf(0, cur)
+
+ -- Optional: fix the width of the preview pane (comment out if not desired)
+ -- vim.cmd("vertical resize 88")
+
+ require("render-markdown").enable() -- enable only in the right window
+ end,
+ desc = "Markdown: open rendered view (right split)",
+ },
+
+ -- Disable rendering in the current window and close the split
+ {
+ "<leader>mR",
+ function()
+ require("render-markdown").disable()
+ -- Close the right split if you're currently in it
+ if #vim.api.nvim_list_wins() > 1 then
+ vim.cmd("wincmd l | close")
+ end
+ end,
+ desc = "Markdown: close rendered view",
+ },
+
+ -- Toggle inline rendering in the current window (no split)
+ {
+ "<leader>mi",
+ function()
+ vim.cmd("RenderMarkdown toggle") -- built-in toggle command
+ end,
+ desc = "Markdown: toggle inline render",
+ },
+ },
+ opts = {
+ -- Good starting defaults; tweak as you like
+ heading = {
+ enabled = true,
+ sign = true,
+ icons = { "▎", "▎", "▎", "▎", "▎", "▎" },
+ },
+ bullet = { enabled = true },
+ checkbox = { enabled = true },
+ code = { enabled = true, sign = true }, -- prettify fenced code blocks
+ link = { enabled = true },
+ table = { enabled = true },
+ },
+ config = function(_, opts)
+ require("render-markdown").setup(opts)
+
+ -- Nice conceal defaults for markdown buffers
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = { "markdown" },
+ callback = function(ev)
+ local win = vim.fn.bufwinid(ev.buf)
+ if win ~= -1 then
+ vim.api.nvim_win_set_option(win, "conceallevel", 2)
+ vim.api.nvim_win_set_option(win, "concealcursor", "nc")
+ end
+ end,
+ })
+ end,
+ },
+}
--- /dev/null
+return {
+ { "nvim-lua/plenary.nvim" },
+ {
+ "nvim-telescope/telescope.nvim",
+ opts = {
+ defaults = { layout_config = { prompt_position = "top" }, sorting_strategy = "ascending" },
+ pickers = { find_files = { hidden = true } },
+ },
+ },
+}
--- /dev/null
+return {
+ { "akinsho/toggleterm.nvim", version = "*", opts = { open_mapping = [[<c-\>]] } },
+}
--- /dev/null
+return {
+ {
+ "nvim-treesitter/nvim-treesitter",
+ build = ":TSUpdate",
+ opts = {
+ ensure_installed = {
+ "bash","python","lua","json","yaml","markdown","markdown_inline",
+ "dockerfile","regex","vim","vimdoc","latex"
+ },
+ highlight = { enable = true },
+ indent = { enable = true },
+ },
+ config = function(_, opts) require("nvim-treesitter.configs").setup(opts) end,
+ },
+}
--- /dev/null
+return {
+ { "folke/trouble.nvim", opts = {} },
+ { "folke/todo-comments.nvim", dependencies = { "nvim-lua/plenary.nvim" }, opts = {} },
+}
--- /dev/null
+return {
+ { "folke/which-key.nvim", opts = {} },
+ { "nvim-tree/nvim-web-devicons" },
+ { "nvim-lualine/lualine.nvim", opts = { options = { theme = "auto" } } },
+ { "catppuccin/nvim", name = "catppuccin", priority = 1000 },
+ { "ellisonleao/gruvbox.nvim", priority = 1000 },
+ { "lukas-reineke/indent-blankline.nvim", main = "ibl", opts = {} },
+}
--- /dev/null
+return {
+ {
+ "s1n7ax/nvim-window-picker",
+ name = "window-picker",
+ version = "2.*",
+ event = "VeryLazy",
+ opts = {
+ autoselect_one = true,
+ include_current = false,
+ filter_rules = {
+ bo = {
+ filetype = { "neo-tree", "neo-tree-popup", "notify", "quickfix" },
+ buftype = { "terminal" },
+ },
+ },
+ -- remove the `highlights = ...` block entirely
+ },
+ },
+}