diff --git a/home/modules/kitty.nix b/home/modules/kitty.nix index bc09353..df965f5 100644 --- a/home/modules/kitty.nix +++ b/home/modules/kitty.nix @@ -46,9 +46,15 @@ in copy_on_select = "yes"; strip_trailing_spaces = "smart"; tab_bar_style = "powerline"; + tab_powerline_style = "round"; tab_bar_align = "left"; tab_bar_edge = "top"; + tab_bar_min_tabs = 1; shell_integration = "enabled"; + active_tab_foreground = "#eeeeee"; + active_tab_background = "#d65d0e"; + inactive_tab_foreground = "#ebdbb2"; + inactive_tab_background = "#202020"; }; keybindings = { "ctrl+shift+n" = "new_window"; diff --git a/home/modules/neovim.nix b/home/modules/neovim.nix index ef5d315..6d010f2 100644 --- a/home/modules/neovim.nix +++ b/home/modules/neovim.nix @@ -1,13 +1,12 @@ { config, pkgs, ... }: - let c = config.theme.colors; in { programs.neovim = { - enable = true; - viAlias = true; - vimAlias = true; + enable = true; + viAlias = true; + vimAlias = true; extraPackages = with pkgs; [ lua-language-server @@ -17,22 +16,125 @@ in fd ]; + # Set mapleader before any plugin config blocks run + extraConfig = '' + let g:mapleader = "\" + let g:maplocalleader = "\" + ''; + plugins = with pkgs.vimPlugins; [ - # Gruvbox theme + # ── Core dependencies ────────────────────────────────────────────── + plenary-nvim + nvim-web-devicons + + # ── Theme ────────────────────────────────────────────────────────── gruvbox-nvim - # Treesitter — better syntax highlighting + # ── Syntax highlighting ──────────────────────────────────────────── { plugin = nvim-treesitter.withAllGrammars; type = "lua"; config = '' - require("nvim-treesitter").setup({ - highlight = { enable = true }, - indent = { enable = true }, - }) - ''; -} - # File explorer + require("nvim-treesitter").setup({ + highlight = { enable = true }, + indent = { enable = true }, + }) + ''; + } + + # ── Completion (must come before lspconfig) ──────────────────────── + luasnip + cmp-nvim-lsp + cmp-buffer + cmp-path + cmp_luasnip + { + plugin = nvim-cmp; + type = "lua"; + config = '' + local cmp = require("cmp") + local luasnip = require("luasnip") + + cmp.setup({ + snippet = { + expand = function(args) + luasnip.lsp_expand(args.body) + end, + }, + mapping = cmp.mapping.preset.insert({ + [""] = 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" }, + { name = "luasnip" }, + { name = "buffer" }, + { name = "path" }, + }), + }) + ''; + } + + # ── LSP ──────────────────────────────────────────────────────────── + { + plugin = nvim-lspconfig; + type = "lua"; + config = '' + local capabilities = require("cmp_nvim_lsp").default_capabilities() + + vim.api.nvim_create_autocmd("LspAttach", { + callback = function(event) + local map = function(keys, func, desc) + vim.keymap.set("n", keys, func, { buffer = event.buf, desc = desc }) + end + map("gd", vim.lsp.buf.definition, "Go to definition") + map("gr", vim.lsp.buf.references, "Find references") + 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("ld", vim.diagnostic.open_float, "Show diagnostics") + map("ln", vim.diagnostic.goto_next, "Next diagnostic") + map("lp", vim.diagnostic.goto_prev, "Prev diagnostic") + end, + }) + + vim.lsp.config("lua_ls", { + capabilities = capabilities, + settings = { + Lua = { + diagnostics = { globals = { "vim" } }, + }, + }, + }) + vim.lsp.enable("lua_ls") + + vim.lsp.config("nil_ls", { + capabilities = capabilities, + }) + vim.lsp.enable("nil_ls") + ''; + } + + # ── File explorer ────────────────────────────────────────────────── { plugin = nvim-tree-lua; type = "lua"; @@ -42,42 +144,73 @@ in ''; } - # Statusline + # ── Statusline ───────────────────────────────────────────────────── { plugin = lualine-nvim; type = "lua"; config = '' require("lualine").setup({ - options = { - theme = "gruvbox-material", - }, + options = { theme = "gruvbox-material" }, }) ''; } - # Icons (needed by nvim-tree and lualine) - nvim-web-devicons + # ── Buffer tabs ──────────────────────────────────────────────────── + { + plugin = bufferline-nvim; + type = "lua"; + config = '' + require("bufferline").setup() + vim.keymap.set("n", "", "BufferLineCycleNext", { desc = "Next buffer" }) + vim.keymap.set("n", "", "BufferLineCyclePrev", { desc = "Prev buffer" }) + vim.keymap.set("n", "x", "bdelete", { desc = "Close buffer" }) + ''; + } - # Fuzzy finder + # ── Fuzzy finder ─────────────────────────────────────────────────── { plugin = telescope-nvim; type = "lua"; config = '' local builtin = require("telescope.builtin") - vim.keymap.set("n", "ff", builtin.find_files) - vim.keymap.set("n", "fg", builtin.live_grep) - vim.keymap.set("n", "fb", builtin.buffers) + vim.keymap.set("n", "ff", builtin.find_files, { desc = "Find files" }) + vim.keymap.set("n", "fg", builtin.live_grep, { desc = "Live grep" }) + vim.keymap.set("n", "fb", builtin.buffers, { desc = "Find buffers" }) + vim.keymap.set("n", "fr", builtin.oldfiles, { desc = "Recent files" }) + vim.keymap.set("n", "fs", builtin.grep_string, { desc = "Find word under cursor" }) ''; } - # Comment toggling + # ── Git ──────────────────────────────────────────────────────────── + { + plugin = gitsigns-nvim; + type = "lua"; + config = '' + require("gitsigns").setup({ + on_attach = function(bufnr) + local gs = require("gitsigns") + local map = function(mode, keys, func, desc) + vim.keymap.set(mode, keys, func, { buffer = bufnr, desc = desc }) + end + map("n", "gn", gs.next_hunk, "Next hunk") + map("n", "gp", gs.prev_hunk, "Prev hunk") + map("n", "gh", gs.preview_hunk, "Preview hunk") + map("n", "gb", gs.blame_line, "Blame line") + map("n", "gs", gs.stage_hunk, "Stage hunk") + map("n", "gr", gs.reset_hunk, "Reset hunk") + end, + }) + ''; + } + + # ── Comment toggling ─────────────────────────────────────────────── { plugin = comment-nvim; type = "lua"; config = ''require("Comment").setup()''; } - # Show indentation guides + # ── Indentation guides ───────────────────────────────────────────── { plugin = indent-blankline-nvim; type = "lua"; @@ -86,61 +219,39 @@ in ]; initLua = '' - -- Leader key - vim.g.mapleader = " " - - -- Colorscheme require("gruvbox").setup({ contrast = "medium", transparent_mode = false, }) vim.cmd("colorscheme gruvbox") - -- Line numbers vim.opt.number = true vim.opt.relativenumber = true + vim.opt.tabstop = 2 + vim.opt.shiftwidth = 2 + vim.opt.expandtab = true + vim.opt.smartindent = true + vim.opt.ignorecase = true + vim.opt.smartcase = true + vim.opt.hlsearch = false + vim.opt.termguicolors = true + vim.opt.signcolumn = "yes" + vim.opt.cursorline = true + vim.opt.scrolloff = 8 + vim.opt.wrap = false + vim.opt.splitright = true + vim.opt.splitbelow = true + vim.opt.clipboard = "unnamedplus" - -- Indentation - vim.opt.tabstop = 2 - vim.opt.shiftwidth = 2 - vim.opt.expandtab = true - vim.opt.smartindent = true - - -- Search - vim.opt.ignorecase = true - vim.opt.smartcase = true - vim.opt.hlsearch = false - - -- UI - vim.opt.termguicolors = true - vim.opt.signcolumn = "yes" - vim.opt.cursorline = true - vim.opt.scrolloff = 8 - vim.opt.wrap = false - vim.opt.splitright = true - vim.opt.splitbelow = true - - -- System clipboard - vim.opt.clipboard = "unnamedplus" - - -- Keymaps local map = vim.keymap.set - - -- Better window navigation map("n", "", "h") map("n", "", "j") map("n", "", "k") map("n", "", "l") - - -- Move lines up/down in visual mode map("v", "J", ":m '>+1gv=gv") map("v", "K", ":m '<-2gv=gv") - - -- Keep cursor centered when scrolling map("n", "", "zz") map("n", "", "zz") - - -- Save and quit map("n", "w", "w") map("n", "q", "q") '';