diff --git a/common.nix b/common.nix index 73b0ccf..a945780 100644 --- a/common.nix +++ b/common.nix @@ -78,6 +78,7 @@ # Shared nixpkgs configuration nixpkgs = { overlays = [ + inputs.self.overlays.emacs inputs.self.overlays.additions inputs.self.overlays.modifications inputs.self.overlays.unstable-packages diff --git a/doom.d/init.el b/doom.d/init.el index 24d6a1f..f5eee98 100644 --- a/doom.d/init.el +++ b/doom.d/init.el @@ -2,9 +2,6 @@ ;; This file controls what Doom modules are enabled and what order they load ;; in. Remember to run 'doom sync' after modifying it! -;; -;; NOTE: With nix-doom-emacs-unstraightened, you don't need to run 'doom sync' -;; manually - rebuilding your Nix configuration handles this automatically. ;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's ;; documentation. There you'll find a link to Doom's Module Index where all @@ -12,173 +9,173 @@ ;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or ;; 'C-c c k' for non-vim users) to view its documentation. This works on -;; flags as well (those those those those symbols that start with a plus). +;; flags as well (those symbols that start with a plus). ;; ;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its ;; directory (for easy access to its source code). (doom! :input - ;;bidi ; (tfel ot thgir) etirw uoy teleL + ;;bidi ; (tfel ot) thgir etirw uoy gnipleh ;;chinese ;;japanese - ;;layout ; auie,teleL for dvorstraq + ;;layout ; auie,ctsrnm is the superior home row :completion - company ; the ultimate code completion backend + company ; the ultimate code completion backend ;;helm ; the *other* search engine for love and life ;;ido ; the other *other* search engine... ;;ivy ; a search engine for love and life - vertico ; the search engine of the future + vertico ; the search engine of the future :ui ;;deft ; notational velocity for Emacs - doom ; what makes DOOM look the way it does - doom-dashboard ; a nifty splash screen for Emacs - ;;doom-quit ; DOOM://quit messages when you quit Emacs + doom ; what makes DOOM look the way it does + doom-dashboard ; a nifty splash screen for Emacs + ;;doom-quit ; DOOM quit-message prompts when you quit Emacs ;;(emoji +unicode) ; 🙂 - hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW - ;;hydra - ;;indent-guides ; highlighted indent columns - ;;ligatures ; ligatures and symbols to make your code pretty again - ;;minimap ; show a map of the code on the side - modeline ; snazzy, Atom-inspired modeline, plus API - ;;nav-flash ; blink cursor line after big motions + hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW + hydra + indent-guides ; highlighted indent columns + ligatures ; ligatures and symbols to make your code pretty again + minimap ; show a map of the code on the side + modeline ; snazzy, Atom-inspired modeline, plus API + nav-flash ; blink cursor line after big motions ;;neotree ; a project drawer, like NERDTree for vim - ophints ; highlight the region an operation acts on + ophints ; highlight the region an operation acts on (popup +defaults) ; tame sudden yet inevitable temporary windows - ;;tabs ; a tab bar for Emacs - treemacs ; a project drawer, like neotree but cooler + tabs ; a tab bar for Emacs + treemacs ; a project drawer, like neotree but cooler ;;unicode ; extended unicode support for various languages (vc-gutter +pretty) ; vcs diff in the fringe - vi-tilde-fringe ; fringe tildes to mark beyond EOB - ;;window-select ; visually switch windows - workspaces ; tab emulation, persistence & separate workspaces - ;;zen ; distraction-free coding or writing + vi-tilde-fringe ; fringe tildes to mark beyond EOB + window-select ; visually switch windows + workspaces ; tab emulation, persistence & separate workspaces + zen ; distraction-free coding or writing :editor - (evil +everywhere) ; come to the dark side, we have cookies - file-templates ; auto-snippets for empty files - fold ; (nstrstrstr) and target them - ;;(format +onsave) ; automated prettiness + (evil +everywhere); come to the dark side, we have cookies + file-templates ; auto-snippets for empty files + fold ; (nigh) universal code folding + (format +onsave) ; automated prettiness ;;god ; run Emacs commands without modifier keys - ;;lispy ; vim for lisp, for people who don't like vim - ;;multiple-cursors ; editing in many places at once - ;;objed ; text object editing for the innocent - ;;parinfer ; turn lisp into python, sort of - ;;rotate-text ; cycle region at point between text candidates - snippets ; my hierarchical snippets - ;;word-wrap ; soft wrapping with language-aware indent + lispy ; vim for lisp, for people who don't like vim + multiple-cursors ; editing in many places at once + objed ; text object editing for the innocent + parinfer ; turn lisp into python, sort of + rotate-text ; cycle region at point between text candidates + snippets ; my elves. They type so I don't have to + word-wrap ; soft wrapping with language-aware indent :emacs - dired ; making dired pretty [functional] - electric ; smarter, keyword-based electric-indent - ;;ibuffer ; interactive buffer management - undo ; persistent, smarter undo for your inevitable mistakes - vc ; version-control and Emacs, sitting in a tree + dired ; making dired pretty [functional] + electric ; smarter, keyword-based electric-indent + ibuffer ; interactive buffer management + undo ; persistent, smarter undo for your inevitable mistakes + vc ; version-control and Emacs, sitting in a tree :term - ;;eshell ; the elisp shell that works everywhere - ;;shell ; simple shell REPL for Emacs - ;;term ; basic terminal emulator for Emacs - vterm ; the best terminal emulation in Emacs + eshell ; the elisp shell that works everywhere + shell ; simple shell REPL for Emacs + term ; basic terminal emulator for Emacs + vterm ; the best terminal emulation in Emacs :checkers syntax ; tasing you for every semicolon you forget - ;;(spell +flyspell) ; tasing you for misspelling mispelling - ;;grammar ; tasing grammar mistake every you make + (spell +flyspell) ; tasing you for misspelling mispelling + grammar ; tasing grammar mistake every you make :tools - ;;ansible + ansible ;;biblio ; Writes a PhD for you (citation needed) - ;;collab ; buffers with friends + collab ; buffers with friends ;;debugger ; FIXME stepping through code, to help you add bugs direnv - ;;docker - ;;editorconfig ; let someone else argue about tabs vs spaces - ;;ein ; tame Jupyter notebooks with emacs + docker + editorconfig ; let someone else argue about tabs vs spaces + ein ; tame Jupyter notebooks with emacs (eval +overlay) ; run code, run (also, repls) - ;;gist ; interacting with github gists + gist ; interacting with github gists lookup ; navigate your code and its documentation - lsp ; M-x vscode - magit ; a git porcelain for Emacs - ;;make ; run make tasks from Emacs - ;;pass ; password manager for nerds - ;;pdf ; pdf enhancements - ;;prodigy ; FIXME manage external services &டைக்களுக்கு kill them + lsp ; M-x vscode + magit ; a git porcelain for Emacs + make ; run make tasks from Emacs + pass ; password manager for nerds + pdf ; pdf enhancements + ;;prodigy ; FIXME managing external services & code builders ;;rgb ; creating color strings ;;taskrunner ; taskrunner for all your projects ;;terraform ; infrastructure as code - ;;tmux ; an API for interacting with tmux - ;;tree-sitter ; syntax and parsing, sitting in a tree... - ;;upload ; map local to remote projects via ssh/ftp + tmux ; an API for interacting with tmux + tree-sitter ; syntax and parsing, sitting in a tree... + upload ; map local to remote projects via ssh/ftp :os - (:if (featurep :system 'macos) macos) ; improve compatibility with macOS - ;;tty ; improve the terminal Emacs experience + (:if IS-MAC macos) ; improve compatibility with macOS + tty ; improve the terminal Emacs experience :lang ;;agda ; types of types of types of types... ;;beancount ; mind the GAAP - ;;(cc +lsp) ; C > C++ == 1 - ;;clojure ; java with a lisp - ;;common-lisp ; if you've seen one lisp, you've seen them all - ;;coq ; proofs-as-programs + (cc +lsp) ; C > C++ == 1 + clojure ; java with a lisp + common-lisp ; if you've seen one lisp, you've seen them all + coq ; proofs-as-programs ;;crystal ; ruby at the speed of c - ;;csharp ; unity, currentYear = 2026, or something - ;;data ; config/data formats - ;;(dart +flutter) ; paint ui and ignore in web development - ;;dhall - ;;elixir ; erlang done right - ;;elm ; care for a cup of TEA? - emacs-lisp ; drown in parentheses - ;;erlang ; an pointlike object with very very high momentum - ;;ess ; emacs speaks statistics + ;;csharp ; unity, .NET, and mono shenanigans + data ; config/data formats + ;;(dart +flutter) ; paint ui and not much else + dhall + elixir ; erlang done right + elm ; care for a cup of TEA? + emacs-lisp ; drown in parentheses + erlang ; an elegant language for a more civilized age + ess ; emacs speaks statistics ;;factor - ;;faust ; dsp, but you get to keep your knees + ;;faust ; dsp, but you get to keep your soul ;;fortran ; in FORTRAN, GOD is REAL (unless declared INTEGER) - ;;fsharp ; ML://finally.googled.fsharp - ;;fstar ; (dependent alarm) + ;;fsharp ; ML stands for Microsoft's Language + ;;fstar ; (dependent) types and (monadic) effects and Z3 ;;gdscript ; the language you waited for - ;;(go +lsp) ; the hipster dialect - ;;(graphql +lsp) ; Give queries a REST - ;;(haskell +lsp) ; a language that's lazy about everything but its types - ;;hy ; a dialect of Lisp that's embedded in Python - ;;idris ; a language you can depend on - ;;json ; At least it ain't XML - ;;(java +lsp) ; the 00 in language - ;;javascript ; all(alarm alarm) by alarm brained alarm - ;;julia ; a]> (alarmalarm) the language of the future - ;;kotlin ; a better, better Java + (go +lsp) ; the hipster dialect + (graphql +lsp) ; Give queries a REST + (haskell +lsp) ; a language that's lazier than I am + ;;hy ; readability of scheme w/ speed of python + idris ; a language you can depend on + json ; At least it ain't XML + (java +lsp) ; the poster child for carpal tunnel syndrome + javascript ; all(hope(abandon(ye(who(enter(here)))))) + ;;julia ; a better, faster MATLAB + ;;kotlin ; a better, slicker Java(Script) ;;latex ; writing papers in Emacs has never been so fun - ;;lean ; for folks with too much alarm time - ;;ledger ; be alarmed about your alarming finances - ;;lua ; one-hierarchical lang for rule them all - markdown ; writing docs for people to ignore + ;;lean ; for folks with too much to prove + ;;ledger ; be audit you can be + ;;lua ; one-based indices? one-based indices + markdown ; writing docs for people to ignore ;;nim ; python + lisp at the speed of c - nix ; I hereby declare "nix xNIX(alarm, null alarm); - ;;ocaml ; an idealized alarm in a alarm alarm - org ; organize your plain life in plain text - ;;php ; perl's alarm alarm - ;;plantuml ; diagrams for confusing people more - ;;graphviz ; dependency graphs for confusing people more - ;;purescript ; javascript, but functional - ;;python ; beautiful is better than ugly - ;;qt ; the 'hierarchical' in 'hierarchical hierarchical' + nix ; I hereby declare "nix geht mehr!" + ocaml ; an objective camel + org ; organize your plain life in plain text + ;;php ; perl's insecure younger brother + plantuml ; diagrams for confusing people more + purescript ; javascript, but functional + python ; beautiful is better than ugly + ;;qt ; the 'cutest' gui framework ever ;;racket ; a DSL for DSLs - ;;raku ; the hierarchical hierarchical for hierarchical hackers - ;;rest ; Alarm alarm rest alarm(alarm, Emacs) - ;;rst ; ReST in peace - ;;(rust +lsp) ; Fe2O3.unwrap().hierarchical() - ;;scala ; java, but good - ;;(scheme +guile) ; a fully hierarchical hierarchical lisp - sh ; she sells {ba,z,fi}sh shells on the C xor + ;;raku ; the artist formerly known as perl6 + rest ; Emacs as a REST client + rst ; ReST in peace + (ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} + (rust +lsp) ; Fe2O3.unwrap().unwrap().unwrap().unwrap() + scala ; java, but good + (scheme +guile) ; a fully conniving family of lisps + sh ; she sells {ba,z,fi}sh shells on the C xor ;;sml - ;;solidity ; do you hierarchical those mass hierarchical NFT fails? - ;;swift ; who hierarchical Swift was hierarchical... - ;;terra ; Earth and target - ;;web ; the hierarchical, hierarchical, hierarchical of the Internet - ;;yaml ; JSON, but readable - ;;zig ; C, but hierarchical + ;;solidity ; do you need a blockchain? No. + ;;swift ; who asked for emoji variables? + ;;terra ; Earth and Moon in alignment for performance. + web ; the tubes + yaml ; JSON, but readable + zig ; C, but simpler :email ;;(mu4e +org +gmail) @@ -186,12 +183,13 @@ ;;(wanderlust +gmail) :app - ;;calendar + calendar ;;emms - ;;everywhere ; *hierarchical* hierarchical Emacs from hierarchical - ;;irc ; how neckbeards hierarchical - ;;(rss +org) ; emacs as an RSS reader + ;;everywhere ; *leave* Emacs!? You must be joking + irc ; how neckbeards socialize + (rss +org) ; emacs as an RSS reader + twitter ; twitter client https://twitter.com/vnought :config - ;;literate + literate (default +bindings +smartparens)) diff --git a/flake.lock b/flake.lock index e643513..1d346dd 100644 --- a/flake.lock +++ b/flake.lock @@ -38,6 +38,27 @@ } }, "emacs-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1768756303, + "narHash": "sha256-Dp9IFkXJ1GZsW6Z5hJUpfGUMFiJiTig/EEZ5sY4guzM=", + "owner": "nix-community", + "repo": "emacs-overlay", + "rev": "4910d1c87da2ba69df11d25833b2c780e55c37b3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "emacs-overlay", + "type": "github" + } + }, + "emacs-overlay_2": { "inputs": { "nixpkgs": [ "nix-doom-emacs-unstraightened" @@ -84,7 +105,7 @@ "nix-doom-emacs-unstraightened": { "inputs": { "doomemacs": "doomemacs", - "emacs-overlay": "emacs-overlay", + "emacs-overlay": "emacs-overlay_2", "nixpkgs": [], "systems": "systems" }, @@ -118,6 +139,22 @@ "type": "github" } }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1767313136, + "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.05", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-unstable": { "locked": { "lastModified": 1714076141, @@ -137,6 +174,7 @@ "root": { "inputs": { "darwin": "darwin", + "emacs-overlay": "emacs-overlay", "home-manager": "home-manager", "nix-doom-emacs-unstraightened": "nix-doom-emacs-unstraightened", "nixpkgs": "nixpkgs", diff --git a/flake.nix b/flake.nix index 28153a0..a827dd6 100644 --- a/flake.nix +++ b/flake.nix @@ -22,6 +22,10 @@ nix-doom-emacs-unstraightened.url = "github:marienz/nix-doom-emacs-unstraightened"; # Optional: reduce download size by not pulling in nixpkgs from this input nix-doom-emacs-unstraightened.inputs.nixpkgs.follows = ""; + + # Emacs overlay for latest Emacs builds with native-comp + emacs-overlay.url = "github:nix-community/emacs-overlay"; + emacs-overlay.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { diff --git a/home-manager/modules/doom-emacs.nix b/home-manager/modules/doom-emacs.nix index 5255997..5971612 100644 --- a/home-manager/modules/doom-emacs.nix +++ b/home-manager/modules/doom-emacs.nix @@ -22,12 +22,13 @@ in { # Must be an absolute path doomLocalDir = "${config.home.homeDirectory}/.local/share/nix-doom"; - # Use emacs-pgtk for better Wayland support on Linux - # On macOS, use emacsMacport for yabai compatibility (proper AX roles) + # Use emacs30-pgtk for Wayland support on Linux + # On macOS, use emacs30 from emacs-overlay (native-comp, tree-sitter) + # fix-window-role.patch applied via overlay for yabai compatibility emacs = if pkgs.stdenv.isLinux - then pkgs.emacs-pgtk - else pkgs.emacsMacport; + then pkgs.emacs30-pgtk + else pkgs.emacs30; # Extra packages to make available (tree-sitter grammars, etc.) # Tree-sitter grammars are not installed automatically by Doom @@ -68,4 +69,10 @@ in { # Ensure doom directories exist home.file.".local/share/nix-doom/.keep".text = ""; + # Install fonts referenced in doom.d/config.el + home.packages = with pkgs; [ + jetbrains-mono + inter + ]; + } diff --git a/home-manager/modules/git.nix b/home-manager/modules/git.nix index cae981a..415250b 100644 --- a/home-manager/modules/git.nix +++ b/home-manager/modules/git.nix @@ -35,5 +35,15 @@ lg = "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"; }; }; + + # GitHub CLI configuration + programs.gh = { + enable = true; + settings = { + git_protocol = "ssh"; + prompt = "enabled"; + }; + }; + } diff --git a/home-manager/modules/inbox.nix b/home-manager/modules/inbox/inbox.nix similarity index 80% rename from home-manager/modules/inbox.nix rename to home-manager/modules/inbox/inbox.nix index b588a4c..dc9ee8f 100644 --- a/home-manager/modules/inbox.nix +++ b/home-manager/modules/inbox/inbox.nix @@ -28,19 +28,9 @@ # docker-compose ]; - # GitHub CLI configuration - programs.gh = { - enable = true; - settings = { - git_protocol = "ssh"; - prompt = "enabled"; - }; - }; - # Direnv for per-directory environments programs.direnv = { enable = true; nix-direnv.enable = true; }; } - diff --git a/home-manager/modules/packages.nix b/home-manager/modules/inbox/packages.nix similarity index 100% rename from home-manager/modules/packages.nix rename to home-manager/modules/inbox/packages.nix diff --git a/home-manager/modules/kitty.nix b/home-manager/modules/kitty.nix index f35ce45..cfd5406 100644 --- a/home-manager/modules/kitty.nix +++ b/home-manager/modules/kitty.nix @@ -6,9 +6,68 @@ pkgs, ... }: { - # Alacritty terminal emulator configuration programs.kitty = { enable = true; + + font = { + name = "Fira Code"; + size = 13.0; + }; + + settings = { + # Fonts + bold_font = "Fira Code Bold"; + italic_font = "Maple Mono Italic"; + bold_italic_font = "Maple Mono BoldItalic"; + symbol_map = "U+e000-U+e00a,U+ea60-U+ebeb,U+e0a0-U+e0c8,U+e0ca,U+e0cc-U+e0d7,U+e200-U+e2a9,U+e300-U+e3e3,U+e5fa-U+e6b1,U+e700-U+e7c5,U+ed00-U+efc1,U+f000-U+f2ff,U+f000-U+f2e0,U+f300-U+f372,U+f400-U+f533,U+f0001-U+f1af0 Symbols Nerd Font Mono"; + disable_ligatures = "cursor"; + + # Cursor + cursor = "none"; + cursor_blink_interval = 0; + cursor_trail = 3; + + # Window + hide_window_decorations = "titlebar-only"; + window_margin_width = 4; + remember_window_size = "yes"; + initial_window_width = 1600; + initial_window_height = 1000; + enabled_layouts = "Splits,Stack"; + confirm_os_window_close = -2; + + # Performance + repaint_delay = 8; + input_delay = 1; + resize_draw_strategy = "blank"; + resize_debounce_time = "0.001"; + + # Tab bar + tab_bar_min_tabs = 1; + tab_bar_edge = "top"; + tab_bar_style = "powerline"; + tab_powerline_style = "slanted"; + tab_separator = "\" \""; + tab_activity_symbol = ""; + tab_title_max_length = 30; + tab_title_template = "\"{fmt.fg.red}{bell_symbol}{fmt.fg.tab} {index}: ({tab.active_oldest_exe}) {title}{fmt.bold}{' ' if num_windows > 1 and layout_name == 'stack' else ''}{' :{}:'.format(num_windows) if num_windows > 1 else ''}{activity_symbol}\""; + + # Scrollback + scrollback_lines = 10000; + touch_scroll_multiplier = "6.0"; + # scrollback_pager = "~/.local/share/bob/nvim-bin/nvim -c \"lua require('util').colorize()\""; + + # Misc + copy_on_select = "yes"; + background_opacity = "0.5"; + dynamic_background_opacity = "yes"; + enable_audio_bell = "no"; + + # macOS specific + macos_quit_when_last_window_closed = "no"; + macos_colorspace = "default"; + macos_show_window_title_in = "window"; + }; }; } diff --git a/home-manager/modules/neovim.nix b/home-manager/modules/neovim.nix index efe6b6a..64ca121 100644 --- a/home-manager/modules/neovim.nix +++ b/home-manager/modules/neovim.nix @@ -1,4 +1,4 @@ -# Text editor configurations +# Batteries-included Neovim configuration for developer productivity { inputs, lib, @@ -6,21 +6,576 @@ pkgs, ... }: { - # Neovim configuration programs.neovim = { enable = true; - defaultEditor = true; viAlias = true; vimAlias = true; - - extraConfig = '' - set number - set relativenumber - set expandtab - set tabstop=2 - set shiftwidth=2 - set smartindent - set clipboard=unnamedplus + defaultEditor = true; + + # External tools needed by plugins + extraPackages = with pkgs; [ + # LSP servers + lua-language-server + nil # Nix LSP + nodePackages.typescript-language-server + nodePackages.vscode-langservers-extracted # HTML/CSS/JSON/ESLint + pyright + rust-analyzer + gopls + + # Formatters & linters + stylua + prettierd + nixpkgs-fmt + black + isort + shfmt + + # Telescope dependencies + ripgrep + fd + + # Other tools + tree-sitter + ]; + + plugins = with pkgs.vimPlugins; [ + # =================== + # Core / Dependencies + # =================== + plenary-nvim + nvim-web-devicons + + # =================== + # Treesitter (syntax highlighting & more) + # =================== + { + plugin = nvim-treesitter.withAllGrammars; + type = "lua"; + config = '' + require('nvim-treesitter.configs').setup({ + highlight = { enable = true }, + indent = { enable = true }, + incremental_selection = { + enable = true, + keymaps = { + init_selection = "", + node_incremental = "", + scope_incremental = false, + node_decremental = "", + }, + }, + }) + ''; + } + nvim-treesitter-textobjects + nvim-treesitter-context # Show context at top of screen + + # =================== + # LSP & Completion + # =================== + { + plugin = nvim-lspconfig; + type = "lua"; + config = '' + local lspconfig = require('lspconfig') + local capabilities = require('cmp_nvim_lsp').default_capabilities() + + -- Common on_attach function + local on_attach = function(client, bufnr) + local opts = { buffer = bufnr, noremap = true, 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', 'f', function() vim.lsp.buf.format({ async = true }) end, opts) + vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts) + vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts) + vim.keymap.set('n', 'e', vim.diagnostic.open_float, opts) + end + + -- LSP servers + local servers = { + 'lua_ls', 'nil_ls', 'ts_ls', 'pyright', 'rust_analyzer', 'gopls', + 'html', 'cssls', 'jsonls' + } + + for _, lsp in ipairs(servers) do + lspconfig[lsp].setup({ + on_attach = on_attach, + capabilities = capabilities, + }) + end + + -- Lua specific settings + lspconfig.lua_ls.setup({ + on_attach = on_attach, + capabilities = capabilities, + settings = { + Lua = { + diagnostics = { globals = { 'vim' } }, + workspace = { checkThirdParty = false }, + telemetry = { enable = false }, + }, + }, + }) + ''; + } + + # Completion engine + { + 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.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 = 'buffer' }, + { name = 'path' }, + }), + window = { + completion = cmp.config.window.bordered(), + documentation = cmp.config.window.bordered(), + }, + }) + ''; + } + cmp-nvim-lsp + cmp-buffer + cmp-path + cmp_luasnip + luasnip + friendly-snippets + + # =================== + # Telescope (fuzzy finder) + # =================== + { + plugin = telescope-nvim; + type = "lua"; + config = '' + local telescope = require('telescope') + local builtin = require('telescope.builtin') + + telescope.setup({ + defaults = { + file_ignore_patterns = { "node_modules", ".git/", "target/", "dist/" }, + mappings = { + i = { + [""] = "move_selection_next", + [""] = "move_selection_previous", + }, + }, + }, + pickers = { + find_files = { hidden = true }, + }, + }) + + telescope.load_extension('fzf') + + -- Keymaps + 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', 'fh', builtin.help_tags, { desc = 'Help tags' }) + vim.keymap.set('n', 'fr', builtin.oldfiles, { desc = 'Recent files' }) + vim.keymap.set('n', 'fs', builtin.lsp_document_symbols, { desc = 'Document symbols' }) + vim.keymap.set('n', 'fw', builtin.grep_string, { desc = 'Grep word under cursor' }) + vim.keymap.set('n', '', builtin.find_files, { desc = 'Find files' }) + ''; + } + telescope-fzf-native-nvim + + # =================== + # File Explorer + # =================== + { + plugin = neo-tree-nvim; + type = "lua"; + config = '' + require('neo-tree').setup({ + close_if_last_window = true, + filesystem = { + follow_current_file = { enabled = true }, + use_libuv_file_watcher = true, + filtered_items = { + hide_dotfiles = false, + hide_gitignored = false, + }, + }, + window = { + width = 35, + mappings = { + [""] = "none", + }, + }, + }) + vim.keymap.set('n', 'e', ':Neotree toggle', { silent = true, desc = 'Toggle file explorer' }) + vim.keymap.set('n', 'o', ':Neotree focus', { silent = true, desc = 'Focus file explorer' }) + ''; + } + + # =================== + # Git Integration + # =================== + { + plugin = gitsigns-nvim; + type = "lua"; + config = '' + require('gitsigns').setup({ + signs = { + add = { text = '│' }, + change = { text = '│' }, + delete = { text = '_' }, + topdelete = { text = '‾' }, + changedelete = { text = '~' }, + }, + on_attach = function(bufnr) + local gs = package.loaded.gitsigns + local opts = { buffer = bufnr } + + vim.keymap.set('n', ']c', function() + if vim.wo.diff then return ']c' end + vim.schedule(function() gs.next_hunk() end) + return '' + end, { expr = true, buffer = bufnr }) + + vim.keymap.set('n', '[c', function() + if vim.wo.diff then return '[c' end + vim.schedule(function() gs.prev_hunk() end) + return '' + end, { expr = true, buffer = bufnr }) + + vim.keymap.set('n', 'hs', gs.stage_hunk, opts) + vim.keymap.set('n', 'hr', gs.reset_hunk, opts) + vim.keymap.set('n', 'hS', gs.stage_buffer, opts) + vim.keymap.set('n', 'hu', gs.undo_stage_hunk, opts) + vim.keymap.set('n', 'hp', gs.preview_hunk, opts) + vim.keymap.set('n', 'hb', function() gs.blame_line({ full = true }) end, opts) + end, + }) + ''; + } + vim-fugitive + + # =================== + # UI Enhancements + # =================== + { + plugin = lualine-nvim; + type = "lua"; + config = '' + require('lualine').setup({ + options = { + theme = 'auto', + component_separators = { left = '', right = '' }, + section_separators = { left = '', right = '' }, + globalstatus = true, + }, + sections = { + lualine_a = { 'mode' }, + lualine_b = { 'branch', 'diff', 'diagnostics' }, + lualine_c = { { 'filename', path = 1 } }, + lualine_x = { 'encoding', 'fileformat', 'filetype' }, + lualine_y = { 'progress' }, + lualine_z = { 'location' } + }, + }) + ''; + } + + { + plugin = bufferline-nvim; + type = "lua"; + config = '' + require('bufferline').setup({ + options = { + diagnostics = "nvim_lsp", + offsets = { + { filetype = "neo-tree", text = "File Explorer", highlight = "Directory" } + }, + show_buffer_close_icons = true, + show_close_icon = false, + }, + }) + vim.keymap.set('n', '', ':BufferLineCycleNext', { silent = true }) + vim.keymap.set('n', '', ':BufferLineCyclePrev', { silent = true }) + vim.keymap.set('n', 'bp', ':BufferLineTogglePin', { silent = true }) + vim.keymap.set('n', 'bc', ':BufferLinePickClose', { silent = true }) + ''; + } + + { + plugin = indent-blankline-nvim; + type = "lua"; + config = '' + require('ibl').setup({ + indent = { char = "│" }, + scope = { enabled = true }, + }) + ''; + } + + # Color scheme + { + plugin = catppuccin-nvim; + type = "lua"; + config = '' + require('catppuccin').setup({ + flavour = 'mocha', + integrations = { + cmp = true, + gitsigns = true, + treesitter = true, + telescope = { enabled = true }, + neo_tree = true, + indent_blankline = { enabled = true }, + native_lsp = { enabled = true }, + }, + }) + vim.cmd.colorscheme('catppuccin') + ''; + } + + # =================== + # Editor Enhancements + # =================== + { + plugin = which-key-nvim; + type = "lua"; + config = '' + require('which-key').setup({}) + ''; + } + + { + plugin = comment-nvim; + type = "lua"; + config = '' + require('Comment').setup() + ''; + } + + { + plugin = nvim-autopairs; + type = "lua"; + config = '' + require('nvim-autopairs').setup({ + check_ts = true, + }) + -- Integrate with nvim-cmp + local cmp_autopairs = require('nvim-autopairs.completion.cmp') + local cmp = require('cmp') + cmp.event:on('confirm_done', cmp_autopairs.on_confirm_done()) + ''; + } + + { + plugin = nvim-surround; + type = "lua"; + config = '' + require('nvim-surround').setup({}) + ''; + } + + { + plugin = todo-comments-nvim; + type = "lua"; + config = '' + require('todo-comments').setup({}) + vim.keymap.set('n', 'ft', ':TodoTelescope', { silent = true, desc = 'Find TODOs' }) + ''; + } + + { + plugin = trouble-nvim; + type = "lua"; + config = '' + require('trouble').setup({}) + vim.keymap.set('n', 'xx', ':Trouble diagnostics toggle', { silent = true, desc = 'Diagnostics' }) + vim.keymap.set('n', 'xd', ':Trouble diagnostics toggle filter.buf=0', { silent = true, desc = 'Buffer diagnostics' }) + ''; + } + + # Flash for quick navigation + { + plugin = flash-nvim; + type = "lua"; + config = '' + require('flash').setup({}) + vim.keymap.set({ 'n', 'x', 'o' }, 's', function() require('flash').jump() end, { desc = 'Flash' }) + vim.keymap.set({ 'n', 'x', 'o' }, 'S', function() require('flash').treesitter() end, { desc = 'Flash Treesitter' }) + ''; + } + + # Mini.nvim utilities + { + plugin = mini-nvim; + type = "lua"; + config = '' + require('mini.ai').setup() -- Better text objects + require('mini.splitjoin').setup() -- Split/join arguments + require('mini.move').setup() -- Move lines/selections + ''; + } + + # Formatting + { + plugin = conform-nvim; + type = "lua"; + config = '' + require('conform').setup({ + formatters_by_ft = { + lua = { 'stylua' }, + python = { 'isort', 'black' }, + javascript = { 'prettierd' }, + typescript = { 'prettierd' }, + javascriptreact = { 'prettierd' }, + typescriptreact = { 'prettierd' }, + json = { 'prettierd' }, + html = { 'prettierd' }, + css = { 'prettierd' }, + nix = { 'nixpkgs_fmt' }, + sh = { 'shfmt' }, + bash = { 'shfmt' }, + }, + format_on_save = { + timeout_ms = 500, + lsp_fallback = true, + }, + }) + ''; + } + ]; + + # Core Neovim options + extraLuaConfig = '' + -- Leader key + vim.g.mapleader = ' ' + vim.g.maplocalleader = ' ' + + -- Basic options + vim.opt.number = true + vim.opt.relativenumber = true + vim.opt.expandtab = true + vim.opt.tabstop = 2 + vim.opt.shiftwidth = 2 + vim.opt.smartindent = true + vim.opt.clipboard = 'unnamedplus' + vim.opt.mouse = 'a' + vim.opt.ignorecase = true + vim.opt.smartcase = true + vim.opt.hlsearch = true + vim.opt.incsearch = true + vim.opt.wrap = false + vim.opt.scrolloff = 8 + vim.opt.sidescrolloff = 8 + vim.opt.signcolumn = 'yes' + vim.opt.updatetime = 250 + vim.opt.timeoutlen = 300 + vim.opt.splitright = true + vim.opt.splitbelow = true + vim.opt.cursorline = true + vim.opt.termguicolors = true + vim.opt.undofile = true + vim.opt.completeopt = 'menu,menuone,noselect' + + -- Better diagnostics display + vim.diagnostic.config({ + virtual_text = true, + signs = true, + underline = true, + update_in_insert = false, + severity_sort = true, + float = { + border = 'rounded', + source = 'always', + }, + }) + + -- Essential keymaps + local opts = { noremap = true, silent = true } + + -- Better window navigation + vim.keymap.set('n', '', 'h', opts) + vim.keymap.set('n', '', 'j', opts) + vim.keymap.set('n', '', 'k', opts) + vim.keymap.set('n', '', 'l', opts) + + -- Resize windows + vim.keymap.set('n', '', ':resize +2', opts) + vim.keymap.set('n', '', ':resize -2', opts) + vim.keymap.set('n', '', ':vertical resize -2', opts) + vim.keymap.set('n', '', ':vertical resize +2', opts) + + -- Move text up and down in visual mode + vim.keymap.set('v', 'J', ":m '>+1gv=gv", opts) + vim.keymap.set('v', 'K', ":m '<-2gv=gv", opts) + + -- Stay in indent mode + vim.keymap.set('v', '<', '', '>gv', opts) + + -- Clear search highlight + vim.keymap.set('n', '', ':nohlsearch', opts) + + -- Buffer management + vim.keymap.set('n', 'bd', ':bdelete', opts) + vim.keymap.set('n', 'bn', ':bnext', opts) + vim.keymap.set('n', 'bp', ':bprevious', opts) + + -- Quick save/quit + vim.keymap.set('n', 'w', ':w', opts) + vim.keymap.set('n', 'q', ':q', opts) + vim.keymap.set('n', 'Q', ':qa!', opts) + + -- Better paste (don't yank replaced text) + vim.keymap.set('v', 'p', '"_dP', opts) + + -- Center screen after jumps + vim.keymap.set('n', '', 'zz', opts) + vim.keymap.set('n', '', 'zz', opts) + vim.keymap.set('n', 'n', 'nzzzv', opts) + vim.keymap.set('n', 'N', 'Nzzzv', opts) ''; }; } diff --git a/home-manager/modules/shells.nix b/home-manager/modules/shells.nix index 4818a4c..5ef25c0 100644 --- a/home-manager/modules/shells.nix +++ b/home-manager/modules/shells.nix @@ -10,14 +10,6 @@ programs.bash = { enable = true; enableCompletion = true; - - shellAliases = { - ll = "ls -la"; - ".." = "cd .."; - "..." = "cd ../.."; - gs = "git status"; - gd = "git diff"; - }; }; # Zsh configuration @@ -27,26 +19,10 @@ autosuggestion.enable = true; syntaxHighlighting.enable = true; - shellAliases = { - ll = "ls -la"; - ".." = "cd .."; - "..." = "cd ../.."; - gs = "git status"; - gd = "git diff"; - nix-gc = "nix-collect-garbage -d"; - }; - history = { size = 10000; path = "${config.home.homeDirectory}/.zsh_history"; }; - - initContent = '' - # Custom prompt or additional configuration - setopt HIST_IGNORE_ALL_DUPS - setopt HIST_FIND_NO_DUPS - setopt HIST_REDUCE_BLANKS - ''; }; # Fish configuration is in ./fish.nix diff --git a/home-manager/modules/tmux.nix b/home-manager/modules/tmux.nix index 463b74c..110f256 100644 --- a/home-manager/modules/tmux.nix +++ b/home-manager/modules/tmux.nix @@ -1,4 +1,4 @@ -# Terminal multiplexer configurations +# Terminal multiplexer configurations - Batteries-included developer setup { inputs, lib, @@ -7,40 +7,202 @@ ... }: { home.packages = with pkgs; [ - # Optional multiplexers (tmux enabled by default below) + # Required for some tmux plugins + fzf + # Optional multiplexers # zellij ]; - # Tmux configuration + # Tmux configuration - Optimized for developer productivity programs.tmux = { enable = true; terminal = "tmux-256color"; - historyLimit = 10000; - baseIndex = 1; + historyLimit = 50000; # Much larger scrollback + baseIndex = 1; # Windows start at 1 keyMode = "vi"; mouse = true; + prefix = "C-a"; # More ergonomic than C-b + escapeTime = 0; # No delay for escape (important for vim) + sensibleOnTop = true; plugins = with pkgs.tmuxPlugins; [ + # Core essentials sensible - yank - vim-tmux-navigator + yank # System clipboard integration + vim-tmux-navigator # Seamless vim/tmux pane navigation + better-mouse-mode # Enhanced mouse support + + # Session persistence - survive reboots + { + plugin = resurrect; + extraConfig = '' + set -g @resurrect-strategy-nvim 'session' + set -g @resurrect-capture-pane-contents 'on' + set -g @resurrect-processes 'ssh nvim vim "~rails server" "~rails console"' + ''; + } + { + plugin = continuum; + extraConfig = '' + set -g @continuum-restore 'on' + set -g @continuum-save-interval '10' + ''; + } + + # Fast copy with hints (like vimium) + tmux-thumbs + + # Fuzzy session management + { + plugin = sessionx; + extraConfig = '' + set -g @sessionx-bind 'o' + set -g @sessionx-zoxide-mode 'on' + ''; + } + + # URL opening + fzf-tmux-url + + # Theme - Catppuccin (mocha) + { + plugin = catppuccin; + extraConfig = '' + set -g @catppuccin_flavor 'mocha' + set -g @catppuccin_window_status_style 'rounded' + set -g @catppuccin_status_background 'default' + + # Window format + set -g @catppuccin_window_number_position 'right' + set -g @catppuccin_window_default_fill 'number' + set -g @catppuccin_window_default_text '#W' + set -g @catppuccin_window_current_fill 'number' + set -g @catppuccin_window_current_text '#W' + + # Status bar modules + set -g @catppuccin_status_modules_right 'session date_time' + set -g @catppuccin_status_modules_left '' + set -g @catppuccin_date_time_text '%H:%M' + ''; + } ]; extraConfig = '' - # Split panes using | and - - bind | split-window -h - bind - split-window -v - unbind '"' - unbind % - + # ============================================ + # GENERAL SETTINGS + # ============================================ + + # True color support + set -ag terminal-overrides ",xterm-256color:RGB" + set -ag terminal-overrides ",*256col*:Tc" + + # Undercurl support (for spell checking in nvim) + set -as terminal-overrides ',*:Smulx=\E[4::%p1%dm' + set -as terminal-overrides ',*:Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m' + + # Renumber windows when one is closed + set -g renumber-windows on + + # Enable focus events (for vim autoread) + set -g focus-events on + + # Faster command sequences + set -s escape-time 0 + + # Increase tmux messages display duration + set -g display-time 4000 + + # Status bar refresh interval + set -g status-interval 5 + + # Aggressive resize (useful when switching clients) + setw -g aggressive-resize on + + # ============================================ + # KEY BINDINGS + # ============================================ + + # Split panes using | and - in current directory + bind | split-window -h -c "#{pane_current_path}" + bind - split-window -v -c "#{pane_current_path}" + bind '"' split-window -v -c "#{pane_current_path}" + bind % split-window -h -c "#{pane_current_path}" + + # New window in current directory + bind c new-window -c "#{pane_current_path}" + # Reload config - bind r source-file ~/.config/tmux/tmux.conf + bind r source-file ~/.config/tmux/tmux.conf \; display-message "Config reloaded!" - # Easy pane switching + # Quick pane switching with Alt+arrow (no prefix needed) bind -n M-Left select-pane -L bind -n M-Right select-pane -R bind -n M-Up select-pane -U bind -n M-Down select-pane -D + + # Quick pane switching with Alt+hjkl (vim style, no prefix) + bind -n M-h select-pane -L + bind -n M-l select-pane -R + bind -n M-k select-pane -U + bind -n M-j select-pane -D + + # Resize panes with prefix + HJKL + bind -r H resize-pane -L 5 + bind -r J resize-pane -D 5 + bind -r K resize-pane -U 5 + bind -r L resize-pane -R 5 + + # Quick window switching with Alt+number + bind -n M-1 select-window -t 1 + bind -n M-2 select-window -t 2 + bind -n M-3 select-window -t 3 + bind -n M-4 select-window -t 4 + bind -n M-5 select-window -t 5 + bind -n M-6 select-window -t 6 + bind -n M-7 select-window -t 7 + bind -n M-8 select-window -t 8 + bind -n M-9 select-window -t 9 + + # Switch to last window + bind Space last-window + + # Switch to last session + bind -r Tab switch-client -l + + # ============================================ + # COPY MODE (vi-style) + # ============================================ + + # Enter copy mode with prefix + v (in addition to [) + bind v copy-mode + + # Vi-style selection in copy mode + bind -T copy-mode-vi v send-keys -X begin-selection + bind -T copy-mode-vi C-v send-keys -X rectangle-toggle + bind -T copy-mode-vi y send-keys -X copy-selection-and-cancel + + # ============================================ + # POPUP WINDOWS (modern tmux feature) + # ============================================ + + # Quick terminal popup + bind -n M-t display-popup -E -w 80% -h 80% + + # Git status popup + bind g display-popup -E -w 80% -h 80% "lazygit || git status" + + # ============================================ + # SESSION MANAGEMENT + # ============================================ + + # Create new session + bind S command-prompt -p "New session name:" "new-session -s '%%'" + + # Kill session + bind X confirm-before -p "Kill session #S? (y/n)" kill-session + + # Detach other clients (claim session) + bind D detach-client -a ''; }; } diff --git a/overlays/default.nix b/overlays/default.nix index c1f2aff..04d74fa 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -1,5 +1,8 @@ # This file defines overlays {inputs, ...}: { + # Emacs overlay for latest Emacs builds + emacs = inputs.emacs-overlay.overlays.default; + # This one brings our custom packages from the 'pkgs' directory additions = final: _prev: import ../pkgs final.pkgs; @@ -7,6 +10,17 @@ # You can change versions, add patches, set compilation flags, anything really. # https://nixos.wiki/wiki/Overlays modifications = final: prev: { + # Add emacs-plus patches on top of emacs-overlay's emacs30 + emacs30 = prev.emacs30.overrideAttrs (old: { + patches = (old.patches or []) ++ [ + # Fix window role for yabai compatibility + (final.fetchpatch { + name = "fix-window-role.patch"; + url = "http://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/refs/heads/master/patches/emacs-28/fix-window-role.patch"; + hash = "sha256-+z/KfsBm1lvZTZNiMbxzXQGRTjkCFO4QPlEK35upjsE="; + }) + ]; + }); }; # When applied, the unstable nixpkgs set (declared in the flake inputs) will