From 66cb17371d1cd6ceb6b6b7beac21076a1226fc74 Mon Sep 17 00:00:00 2001 From: Joe Frikker Date: Wed, 16 Sep 2026 23:38:22 -0400 Subject: [PATCH] Switching to old output files --- home.org | 2326 ------------------------------------- modules/home/default.nix | 749 +++--------- modules/home/emacs.el | 473 ++++---- modules/home/emacs.nix | 137 +++ modules/home/helix.nix | 79 ++ modules/home/starship.nix | 36 + modules/home/tmux.nix | 34 + 7 files changed, 725 insertions(+), 3109 deletions(-) delete mode 100644 home.org create mode 100644 modules/home/emacs.nix create mode 100644 modules/home/helix.nix create mode 100644 modules/home/starship.nix create mode 100644 modules/home/tmux.nix diff --git a/home.org b/home.org deleted file mode 100644 index 958a432..0000000 --- a/home.org +++ /dev/null @@ -1,2326 +0,0 @@ -#+PROPERTY: header-args :mkdirp yes :noweb yes -#+PROPERTY: header-args:elisp :lexical t -* Categories -#+begin_src nix :tangle modules/home/default.nix - {lib, pkgs, config, ...}: let - inherit (lib) - mkIf - mkMerge - ; - cfg = config.jfrikker; - in { - options.jfrikker = { - enable = lib.mkEnableOption "Joe Frikker's configuration"; - desktop.enable = lib.mkEnableOption "Desktop (GUI) applications"; - redbook.enable = lib.mkEnableOption "Redbook-specific configuration"; - - <> - }; - - config = mkIf cfg.enable (mkMerge [ - <> - ]); - } -#+end_src - -* Packages -:PROPERTIES: -:header-args:nix: :noweb-ref nix-configs -:END: - -Home-manager "packages" are simply binaries that are placed on the path, without any attached -configuration. The code blocks in this section contain package lists that go under the packages -section above. - -~SBCL~ is the current leading open-source common lisp compiler. It requires any libraries you want -to be availble to be installed using withPackages. Many lisp projects I work on have this overridden -in a ~nix~ devshell I think I'd also like to use it for scripting / exploratory development, though, -so I'm installing it globally with a useful set of libraries. I should curate this somewhat -carefully so it doesn't get carried away... - -#+BEGIN_SRC nix - { - home.packages = with pkgs; [ - rlwrap - (sbcl.withPackages (ps: with ps; [ - alexandria - cl-markup - cl-mock - clack - clack-handler-woo - dbi - defstar - dexador - fare-csv - group-by - lack-request - lack-response - local-time - log4cl - mito - myway - prove - prove-asdf - replic - serapeum - shasht - slynk - smug - split-sequence - str - sxql-composer - trivia - trivial-mmap - trivial-ws - trivial-open-browser - trivial-timeout - uuid - ])) - ]; - } -#+END_SRC - -[[https://github.com/str4d/rage][~rage~]] is a rust implementation of [[https://pkg.go.dev/filippo.io/age][~age~]]. It implements a simple system for generating public / -private encryption keys, and encrypting / decrypting files with those keys. [[https://github.com/str4d/age-plugin-yubikey][~age-plugin-yubikey~]], as -the name implies, allows generating and storing ~age~ keys on a Yubikey. ~rage~ is required over -~age~ when using ~age.el~, because it supports ~pinentry~. That allows it to prompt for yubikey pins -using a dialog, rather than a command line prompt. - -#+BEGIN_SRC nix - { - home.packages = with pkgs; [ - rage - age-plugin-yubikey - yubikey-manager - ]; - } -#+END_SRC - -~dust~, ~fd~, ~ripgrep~, and ~xh~ are rust re-implementations of a few longtime Linux command-line -tools. They boost better speed, or simply more streamlined features. - -| Command | Original | -| ~dust~ | ~du~ | -| ~fd~ | ~find~ | -| ~ripgrep~ | ~grep~ | -| ~xh~ | ~curl~ | - -#+BEGIN_SRC nix - { - home.packages = with pkgs; [ - dust - fd - ripgrep - tokei - xh - ]; - - programs.eza = { - enable = true; - enableBashIntegration = true; - enableZshIntegration = true; - icons = "auto"; - }; - } -#+END_SRC - -[[https://ledger-cli.org/][~ledger~]] is how I manage my budget. It's a plain-text accounting tool that's really awesome. - -#+BEGIN_SRC nix - { - home.packages = with pkgs; [ - ledger - ]; - } -#+END_SRC - -~nil~ is an LSP server for nix files. - -#+BEGIN_SRC nix - { - home.packages = with pkgs; [ - nil - nixfmt - ]; - } -#+END_SRC - -[[https://github.com/magic-wormhole/magic-wormhole][Magic Wormhole]] is a quick-and-easy way to securely transfer a file or directory between any two -computers in the world, assuming that you have an out-of-band way to communicate a passphrase -between them. Once installed, the actual command is ~wormhole~. - -#+begin_src nix - { - home.packages = with pkgs; [ - magic-wormhole - ]; - } -#+end_src - -These are the other programs I have installed globally. A few (like ~awscli~, ~mysql-client~, etc.) -are still Redbook-specific; ideally I'd find another way to install them. - - #+BEGIN_SRC nix - (mkIf cfg.redbook.enable { - home.packages = with pkgs; [ - openssh - ]; - }) -#+END_SRC - -These programs require a full desktop environment: - -#+BEGIN_SRC nix - (mkIf cfg.desktop.enable { - home.packages = with pkgs; [ - google-chrome - nerd-fonts.jetbrains-mono - nerd-fonts.symbols-only - (iosevka-bin.override { variant = "Aile"; }) - (iosevka-bin.override { variant = "Etoile"; }) - (iosevka-bin.override { variant = "SGr-IosevkaTermSS14"; }) - nerd-fonts.symbols-only - keymapp - proton-pass - wireshark - ]; - - fonts.fontconfig.enable = true; - }) -#+END_SRC - -#+BEGIN_SRC nix - (mkIf (cfg.redbook.enable && cfg.desktop.enable) { - home.packages = with pkgs; [ - linear - notion-app - (slack.overrideAttrs { - version = "4.50.143"; - src = pkgs.fetchurl { - url = "https://downloads.slack-edge.com/desktop-releases/mac/arm64/4.50.143/Slack-4.50.143-macOS.dmg"; - hash = "sha256-f5oYi8NPKXWpDcowFYMUjptbQs4V9hK2bm1TuaQeJn0="; - }; - }) - teams - ]; - }) -#+END_SRC - -* Program Configurations -:PROPERTIES: -:header-args:nix: :noweb-ref nix-configs -:END: - -This section contains applications whose config files will be generated by Home Manager. Home -Manager calls these "programs" (as opposed to "packages", which are just binaries to put on the -path). - -** Simple Programs - -Although Home-Manager generates config files for these programs, they don't need their own dedicated -configuration. I'm not sure what benefit there is to enabling ~jq~ as a Home Manager program, rather -than just as an available package, but there doesn't seem to be a downside. - -#+BEGIN_SRC nix - { - programs = { - bash.enable = true; - gh.enable = true; - home-manager.enable = true; - jq.enable = true; - lazygit.enable = true; - zoxide.enable = true; - }; - } -#+END_SRC - -** Bat - -~bat~ is a less alternative that performs syntax highlighting for a variety of languages. It also -provides syntax-highlighting versions of a few other commands. It's honestly not incredibly useful, -but still nice to have. - -#+BEGIN_SRC nix - { - programs.bat = { - enable = true; - config.style = "numbers"; - }; - } -#+END_SRC - -** Comma - -#+BEGIN_SRC nix - { - programs.nix-index.enable = true; - programs.nix-index-database.comma.enable = true; - } -#+END_SRC - -** Direnv - -~direnv~ allows loading per-project paths as I navigate to their directories on the command line. I -leverage this with the emacs ~envrc~ package. - -#+BEGIN_SRC nix - { - programs.direnv = { - enable = true; - nix-direnv.enable = true; - }; - } -#+END_SRC - -** Fzf - -~fzf~ offers a variety of fuzzy finders right from the shell. I haven't been using this, so I'll -turn it off for now. - -#+BEGIN_SRC nix - { - programs.fzf = { - defaultCommand = "fd --type f"; - fileWidgetCommand = "fd --type f"; - fileWidgetOptions = ["--preview" "'bat --color=always --style=numbers --line-range=:500 {}'"]; - changeDirWidgetCommand = "fd --type d"; - tmux.enableShellIntegration = true; - }; - } -#+END_SRC - -** Git - -We all know what ~git~ does. - -#+BEGIN_SRC nix - { - programs.git = { - enable = true; - settings.user.name = "Joe Frikker"; - settings.user.email = "joe@redbookcattle.com"; - settings.github.user = "joe@redbookcattle.com"; - settings.merge.conflictstyle = "zdiff3"; - }; - - programs.delta = { - enable = true; - enableGitIntegration = true; - }; - } -#+END_SRC - -** Helix - -The best text editor ever? Heresy! - -#+BEGIN_SRC nix - { - programs.helix = { - enable = true; - defaultEditor = true; - - settings.editor = { - auto-format = false; - auto-pairs = false; - auto-save.after-delay.enable = true; - auto-save.after-delay.timeout = 1000; - cursorline = true; - idle-timeout = 0; - rulers = [120]; - text-width = 100; - lsp.goto-reference-include-declaration = false; - cursor-shape.insert = "bar"; - smart-tab.supersede-menu = true; - whitespace.render.newline = "all"; - }; - - settings.keys.normal.space = { - space = "file_picker"; - "," = "buffer_picker"; - # b = "@:sh wezterm cli spawn --cwd . -- tig blame %"; - }; - - - languages.language-server.eslint = { - args = ["--stdio"]; - command = "vscode-eslint-language-server"; - config.nodePath = ""; - config.quiet = false; - config.rulesCustomizations = []; - config.run = "onType"; - config.validate = "on"; - config.codeAction.disableRuleComment.enable = true; - config.codeAction.disableRuleComment.location = "separateLine"; - config.codeAction.showDocumentation.enable = true; - config.problems.shortenToSingleLine = false; - }; - - languages.language-server.jdtls.config.java = { - autobuild.enabled = true; - completion.maxResults = 1000; - format.settings.url = "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml"; - }; - - languages.language-server.rust-analyzer.config.files.watcher = "client"; - - languages.language = [ - { - name = "typescript"; - language-servers = [ "typescript-language-server" "vscode-eslint-language-server" ]; - indent.tab-width = 4; - indent.unit = " "; - } - { - name = "tsx"; - language-servers = [ "typescript-language-server" "vscode-eslint-language-server" ]; - indent.tab-width = 4; - indent.unit = " "; - } - { - name = "java"; - indent.tab-width = 4; - indent.unit = " "; - } - ]; - }; - } -#+END_SRC - -** Nh - -~nh~ is an alternative frontend to home-manager and a few other nix tools that I'm trying out. It -shows what's changing when you update your config, which is nice. - -#+BEGIN_SRC nix - { - programs.nh = { - enable = true; - }; - } -#+END_SRC - - -** Starship - -~starship~ gives me a nice shell prompt, mostly showing the versions of relevant software I have -installed. It's shell-agnostic, so it works with both bash and zsh. - -#+BEGIN_SRC nix - { - programs.starship = { - enable = true; - settings = { - add_newline = false; - container.disabled = true; - git_branch.symbol = " "; - git_status.ahead = "⇡\${count}"; - git_status.behind = "⇣\${count}"; - git_status.diverged = "⇕⇡\${ahead_count}⇣\${behind_count}"; - git_status.format = "([\\[\$conflicted\$staged\$ahead_behind\\]](\$style) )"; - java.symbol = " "; - line_break.disabled = true; - nix_shell.symbol = " "; - nodejs.symbol = " "; - package.disabled = true; - palette = "catppuccin_frappe"; - python.symbol = " "; - package.symbol = " "; - rust.symbol = " "; - docker_context.disabled = true; - shell.disabled = false; - }; - }; - } -#+END_SRC - -** Tmux - -I'm using ~wezterm~ instead of ~tmux~ for now, so this is disabled, but I'm keeping the config here -in case I need it later. - -#+BEGIN_SRC nix - { - programs.tmux = { - baseIndex = 1; - clock24 = true; - customPaneNavigationAndResize = true; - historyLimit = 50000; - enable = true; - escapeTime = 0; - keyMode = "vi"; - mouse = true; - shell = pkgs.zsh + "/bin/zsh"; - shortcut = "Space"; - tmuxinator.enable = true; - terminal = "xterm-256color"; - extraConfig = '' - bind-key -T copy-mode-vi 'v' send -X begin-selection - bind-key -T copy-mode-vi 'y' send -X copy-selection-and-cancel - - bind-key -T launcher g popup -h 100% -w 100% -E lazygit - bind-key g switch-client -Tlauncher - set -g default-command "" - ''; - }; - } -#+END_SRC -** Wezterm -:PROPERTIES: -:header-args:lua: :noweb-ref wezterm-config -:END: - -~wezterm~ is a fast terminal emulator and multiplexer. It is configured using lua. We'll define its -config in two blocks, so we can get lua syntax highlighting in org. First, the nix configuration: - -#+BEGIN_SRC nix - (mkIf cfg.desktop.enable { - programs.wezterm = { - enable = true; - extraConfig = '' - local config = {} - config.default_prog = { '${pkgs.zsh}/bin/zsh', '-l'} - config.font_size = ${if pkgs.stdenv.isDarwin then "13.0" else "10.0"} - '' + builtins.readFile ./wezterm.lua; - }; - }) -#+END_SRC - -Then the real lua config: - -#+begin_src lua :tangle modules/home/wezterm.lua :noweb-ref nil - local config = {} - - <> - - return config -#+end_src - -#+BEGIN_SRC lua - config.font = wezterm.font 'Iosevka Term SS14' - config.font_size = 13.0 - -- config.font = wezterm.font("JetBrains Mono") - config.inactive_pane_hsb = { - saturation = 0.5, - brightness = 0.5, - } - config.pane_focus_follows_mouse = true - config.switch_to_last_active_tab_when_closing_tab = true - config.tab_bar_at_bottom = true - config.use_fancy_tab_bar = false - config.window_decorations = "RESIZE" -#+END_SRC - -I use a workspace switcher plugin, bouned to ~cmd-s~, that lets me quickly switch between -per-project workspaces using a single OS window. - -#+BEGIN_SRC lua - local workspace_switcher = wezterm.plugin.require("https://github.com/MLFlexer/smart_workspace_switcher.wezterm") - workspace_switcher.zoxide_path='~/.nix-profile/bin/zoxide' -#+END_SRC - -Set up a default window layout when switching to a workspace that hasn't been opened in this session -yet. This will create three tabs, caled "edit", "run", and "term", and open helix in the first -one. I use "edit" for running the editor (obviously), "run" for building / running the application, -and "term" for miscellaneous other tasks. I sometimes split the "run" window into multiple panes if -the project requires multiple things to run simultaneously. - -#+BEGIN_SRC lua - wezterm.on('smart_workspace_switcher.workspace_switcher.created', function(window, path, workspace) - local editor = window:active_tab() - editor:set_title("edit") - editor:panes()[1]:send_text("hx\n") - local run = window:spawn_tab {} - run:set_title "run" - local term = window:spawn_tab {} - term:set_title "term" - editor:activate() - end) - -#+END_SRC - -Put the current workspace name in the lower-right corner of the window. Taken from -[https://alexplescan.com/posts/2024/08/10/wezterm/]. - -#+BEGIN_SRC lua - local function segments_for_right_status(window) - return { - window:active_workspace(), - } - end - - wezterm.on('update-status', - function(window, _) - local SOLID_LEFT_ARROW = utf8.char(0xe0b2) - local segments = segments_for_right_status(window) - - local color_scheme = window:effective_config().resolved_palette - -- Note the use of wezterm.color.parse here, this returns - -- a Color object, which comes with functionality for lightening - -- or darkening the colour (amongst other things). - local bg = wezterm.color.parse(color_scheme.background) - local fg = color_scheme.foreground - - -- Each powerline segment is going to be coloured progressively - -- darker/lighter depending on whether we're on a dark/light colour - -- scheme. Let's establish the "from" and "to" bounds of our gradient. - local gradient_to, gradient_from = bg - gradient_from = gradient_to:lighten(0.2) - - -- Yes, WezTerm supports creating gradients, because why not?! Although - -- they'd usually be used for setting high fidelity gradients on your terminal's - -- background, we'll use them here to give us a sample of the powerline segment - -- colours we need. - local gradient = wezterm.color.gradient( - { - orientation = 'Horizontal', - colors = { gradient_from, gradient_to }, - }, - #segments -- only gives us as many colours as we have segments. - ) - - -- We'll build up the elements to send to wezterm.format in this table. - local elements = {} - - for i, seg in ipairs(segments) do - local is_first = i == 1 - - if is_first then - table.insert(elements, { Background = { Color = 'none' } }) - end - table.insert(elements, { Foreground = { Color = gradient[i] } }) - table.insert(elements, { Text = SOLID_LEFT_ARROW }) - - table.insert(elements, { Foreground = { Color = fg } }) - table.insert(elements, { Background = { Color = gradient[i] } }) - table.insert(elements, { Text = ' ' .. seg .. ' ' }) - end - - window:set_right_status(wezterm.format(elements)) - end) -#+END_SRC - -Now we'll set up some keybindings: - -| Key | Action | -| s-s | Switch Workspace | -| s-- | Vertical Split | -| s-\ | Horizontal split | -| s-hjkl | Switch to the pane in that direction | -| s-g | Launch ~lazygit~ in a new tab. The tab closes when ~lazygit~ exits. | -| s-z | Toggle full-screen zoom for the active pane. | -| s-t | Launch a terminal in a new tab. | -| s-d | Close the current tab (with confirmation). | -| s-123456789 | Switch to the tab with the corresponding number. | - -#+BEGIN_SRC lua - config.leader = { key = ' ', mods = 'CTRL', timeout_milliseconds = 10000 } - config.keys = { - { - key = "s", - mods = "CMD", - action = workspace_switcher.switch_workspace(), - }, - { - key = "-", - mods = "CMD", - action = wezterm.action.SplitVertical {}, - }, - { - key = "\\", - mods = "CMD", - action = wezterm.action.SplitHorizontal {}, - }, - { - key = "h", - mods = "CMD", - action = wezterm.action.ActivatePaneDirection "Left", - }, - { - key = "j", - mods = "CMD", - action = wezterm.action.ActivatePaneDirection "Down", - }, - { - key = "k", - mods = "CMD", - action = wezterm.action.ActivatePaneDirection "Up", - }, - { - key = "l", - mods = "CMD", - action = wezterm.action.ActivatePaneDirection "Right", - }, - { - key = "g", - mods = "CMD", - action = wezterm.action.SpawnCommandInNewTab { - args = { wezterm.home_dir .. "/.nix-profile/bin/zsh", "-l", "-i", "-c", "lazygit" } - } - }, - { - key = "t", - mods = "CMD", - action = wezterm.action.SpawnCommandInNewTab {} - }, - { - key = 'z', - mods = 'CMD', - action = wezterm.action.TogglePaneZoomState, - }, - { - key = 'd', - mods = 'CMD', - action = wezterm.action.CloseCurrentTab { confirm = true }, - }, - } - - for i = 1, 9 do - -- CTRL+ALT + number to activate that tab - table.insert(config.keys, { - key = tostring(i), - mods = 'LEADER', - action = wezterm.action.ActivateTab(i - 1), - }) - end -#+END_SRC - -** Kitty -#+BEGIN_SRC nix - (mkIf cfg.desktop.enable { - programs.kitty = { - enable = true; - font = { - name = "Iosevka Term SS14"; - package = pkgs.iosevka-bin.override { variant = "SGr-IosevkaTermSS14"; }; - size = 13; - }; - themeFile = "Catppuccin-Frappe"; - keybindings = { - "ctrl+1" = "goto_tab 1"; - "ctrl+2" = "goto_tab 2"; - "ctrl+3" = "goto_tab 3"; - "ctrl+4" = "goto_tab 4"; - "ctrl+5" = "goto_tab 5"; - "ctrl+6" = "goto_tab 6"; - "ctrl+7" = "goto_tab 7"; - "ctrl+8" = "goto_tab 8"; - "ctrl+9" = "goto_tab 9"; - }; - }; - }) -#+END_SRC - -** Ghostty -Disabled until I can get it working on both linux and mac (if I want to). - -#+BEGIN_SRC nix - { - programs.ghostty = { - enable = false; - package = pkgs.ghostty-bin; - settings = { - adjust-cell-height = "15%"; - font-family = "Iosevka Term SS14"; - font-size = 13; - }; - }; - } -#+END_SRC - -** Zsh - -This is my default shell for now. Unfortunately, nix requires a posix-compliant shell, so bash or -zsh it is. - -#+BEGIN_SRC nix - { - programs.zsh = { - enable = true; - oh-my-zsh.enable = true; - syntaxHighlighting.enable = true; - autosuggestion.enable = true; - historySubstringSearch.enable = true; - history.share = false; - cdpath = ["~/source"]; - }; - } -#+END_SRC - -Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh from nix-shell. - -#+BEGIN_SRC nix - { - programs.zsh.plugins = [ - { - name = "zsh-nix-shell"; - file = "nix-shell.plugin.zsh"; - src = pkgs.fetchFromGitHub { - owner = "chisui"; - repo = "zsh-nix-shell"; - rev = "v0.7.0"; - sha256 = "149zh2rm59blr2q458a5irkfh82y3dwdich60s9670kl3cl5h2m1"; - }; - } - ]; - } -#+END_SRC - -* Emacs -:PROPERTIES: -:header-args:nix: :noweb-ref nix-configs -:header-args:elisp: :tangle modules/home/emacs.el -:END: - -Emacs is a huge portion of this configuration, so although it's a home-manager "program", I'm -breaking it out into its own top-level section. Many of the sections here have two code blocks: a -nix code block listing the emacs packages to include, and an elisp code block to configure them in -emacs. - -We'll put the actual emacs configs into separate files, rather than into nix string literals. This -helps with a few places where nix's string interpolation was causing problems. - -#+BEGIN_SRC nix - (mkIf cfg.desktop.enable { - programs.emacs = { - enable = true; - extraConfig = builtins.readFile ./emacs.el; - }; - }) -#+END_SRC - -** Keybindings -These keybindings are scattered throughout this file, I'm collecting them all here to make it easier -to track them. - - | Key | Normal Key | Action | Mode | - | s-b | | [[id:6D1A8B35-3093-4DA6-A5AA-2A0D45112BAB][Switch project buffer]] | | - | s-B | | [[id:6D1A8B35-3093-4DA6-A5AA-2A0D45112BAB][Switch buffer]] | | - | s-f | C-x p f | [[id:A2F7FF5C-AFCC-44D4-9AA5-3498F6D6D5E0::workspace_keybinds][Open file in current project]] | | - | s-F | C-x p p | [[id:A2F7FF5C-AFCC-44D4-9AA5-3498F6D6D5E0::workspace_keybinds][Switch project]] | | - | S-s | C-x t RET | [[id:A2F7FF5C-AFCC-44D4-9AA5-3498F6D6D5E0::workspace_keybinds][Switch tab]] | | - | C-c A | | [[id:3B0BFCDC-DFD5-42A0-97D8-3B032B128AF7][Org agenda]] | | - | C-c a | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Eglot code actions]] | prog | - | C-c c | | Claude Code | | - | C-c C | | [[id:3B0BFCDC-DFD5-42A0-97D8-3B032B128AF7][Org capture]] | | - | C-c D | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Show flymake diagnostics]] | prog | - | C-c h | | [[id:6D1A8B35-3093-4DA6-A5AA-2A0D45112BAB][Consult shell history]] | | - | C-c i | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Eglot find implementation]] | prog | - | C-c l | | [[id:3B0BFCDC-DFD5-42A0-97D8-3B032B128AF7][Org store link]] | | - | C-c n | | [[id:3B0BFCDC-DFD5-42A0-97D8-3B032B128AF7][Org Roam prefix]] | | - | C-c r | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Eglot rename]] | prog | - | C-c u | | [[id:5CCB676B-9FF0-47B4-8AF2-10FD3899A646][Cycle string inflection]] | various | - | C-c M-g | | [[id:4F426E77-CE03-4BF8-8D1B-7791C7C02B28][Magit file dispatch]] | | - -** Global Settings -These are just some general settings that apply everywhere. First, a few packages: - -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - ace-window - doom-modeline - free-keys - ligature - mixed-pitch - popper - ]; - }; - } -#+END_SRC - -Turn off some default settings that I don't find useful. - -#+BEGIN_SRC elisp - (setopt save-interprogram-paste-before-kill t) - (setopt enable-recursive-minibuffers t) - (setopt line-spacing 0.15) - - (setopt mouse-wheel-tilt-scroll t) - (setq-default truncate-lines t) - - (setopt tab-bar-show nil) - (tab-bar-mode) -#+END_SRC - -Don't show the welcome screen, and keep the scratch buffer empty by default. These are not useful -after you've started emacs more than a few times. - -#+begin_src elisp - (setopt inhibit-startup-screen t) - (setopt initial-scratch-message nil) -#+end_src - -Turn off all the ugly graphical crap. - -#+begin_src elisp - (menu-bar-mode -1) - (scroll-bar-mode -1) - (tool-bar-mode -1) -#+end_src - -I don't find emacs's backup files useful, and they seem to litter my directories. I could maybe see -a use if I wasn't using git. But, git is so easy to use, that I pretty much use it everywhere. I -don't need a poor man's local history. - -Also, why do I have to set so many variables just to get emacs to stop putting extra files -everywhere? - -#+begin_src elisp - (setopt make-backup-files nil) - (setopt create-lockfiles nil) - (setopt auto-save-default nil) -#+end_src - -Turn on ~view-mode~ automatically when the buffer is read-only. This just helps out with navigation -a bit. I almost want to open files read-only by default, just so I can use ~view-mode~ (maybe with -some other keybindings) all the time. But, I think one more invisible flag to manage is a bit much. - -#+begin_src elisp - (setopt view-read-only t) -#+end_src - -I don't really do anything with sentences. But, I also never put two spaces after a period. So, at -least this setting is correct. - -#+begin_src elisp - (setopt sentence-end-double-space nil) -#+end_src - -I kill whole lines *way* more than partial lines. If the cursor is at the front of a line, this will -just kill the whole line instead of requiring two ~C-k C-k~ presses to make it happen. I'm not sure -when I'd ever want it to kill the line *without* the trailing newline, but I guess the one time ever -that I do, I'll have to press two more keys :( - -#+begin_src elisp - (setopt kill-whole-line t) -#+end_src - -Show column numbers in the modeline. Now that I think about it, I'm not sure why I want this. I -guess I could try turning it off someday? - -#+begin_src elisp - (column-number-mode t) -#+end_src - -Make sure which-key mode is always on. The on-the-fly keymap help it gives is indespensable. - -#+BEGIN_SRC elisp - (which-key-mode) -#+END_SRC - -Automatically refresh ~dired~ buffers when switching to them, instead of requiring an explicit -refresh. - -#+BEGIN_SRC elisp - (setopt dired-auto-revert-buffer t) -#+END_SRC - -Similarly, highlighting the line the cursor is on is really useful for figuring out where you are, -especially on monitors with a lot of vertical space. - -#+BEGIN_SRC elisp - (global-hl-line-mode) -#+END_SRC - -From the docs: "If ‘complete’, TAB first tries to indent the current line, and if the line was -already indented, then try to complete the thing at point." Recommended by [[https://github.com/minad/corfu][the ~corfu~ readme]]. - -#+BEGIN_SRC elisp - (setopt tab-always-indent 'complete) -#+END_SRC - -Set the cursor to be a bar. I'm not sure what the benefit would be of a block cursor, since the -current character isn't really "selected" like it would be in Helix. I switch this to a box when in -god mode / read-only buffers, like vim would. - -#+BEGIN_SRC elisp - (setq-default cursor-type 'bar) -#+END_SRC - -Repeat-mode takes care of eliminating some annoying duplicate keypresses. I mostly use repeat-mode -for two-character commands that need to be repeated; ~devil~ also has some single-key-with-modifier -repeats set up that only apply in that mode. - -#+BEGIN_SRC elisp - (repeat-mode) -#+END_SRC - - Save the minibuffer history to disk, so recently-used options show up at the top of the picker - across restarts. - -#+BEGIN_SRC elisp - (savehist-mode) -#+END_SRC - -Let's unbind some builtin keys that I don't use. I'm not sure if I'll replace them with anything or not, but at a minimum it'll avoid accidental keypresses and clean up which-key output. - -#+begin_src elisp - (require 'bind-key) - (unbind-key "C-x RET") - (unbind-key "C-x #") - (unbind-key "C-x 5") - (unbind-key "C-x 6") - (unbind-key "C-x \\") - (unbind-key "C-x `") - (unbind-key "C-x m") - (unbind-key "C-x l") - (unbind-key "C-x i") - (unbind-key "C-x u") - (unbind-key "C-x @") - (unbind-key "C-x C-b") - (unbind-key "C-x C-d") - (unbind-key "C-x C-l") - (unbind-key "C-x C-p") - (unbind-key "C-x C-u") - (unbind-key "C-x C-z") -#+end_src - -The doom modeline looks nice. I'm turning off IRC notifications, because it was slow to update (and -it would update after every command, slowing down everything). I don't need the workspace name when -I have the tab bar at the top of the window. The other settings just clear up some space, and make -things look at little nicer. - -#+BEGIN_SRC elisp - (setopt doom-modeline-irc nil) - (setopt doom-modeline-workspace-name nil) - (setopt doom-modeline-buffer-encoding nil) - (setopt doom-modeline-vcs-max-length 30) - (doom-modeline-mode) -#+END_SRC - -Ligature mode combines character sequences like <= into a single on-screen glyph. It looks kind of -cool, so I'm turning it on in prog mode. The enabled ligatures are font-specific, taken from [[https://github.com/mickeynp/ligature.el/wiki#jetbrains-mono][the -ligature wiki]]. - -#+BEGIN_SRC elisp :noweb no - (ligature-set-ligatures 'prog-mode - '("--" "---" "==" "===" "!=" "!==" "=!=" - "=:=" "=/=" "<=" ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!" - "??" "???" "?:" "?." "?=" "<:" ":<" ":>" ">:" "<:<" "<>" "<<<" ">>>" - "<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##" "###" "####" - "#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:" "#!" "#=" "^=" "<$>" "<$" - "$>" "<+>" "<+" "+>" "<*>" "<*" "*>" "" "/>" "" "->" "->>" "<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>" - "==>" "=>" "=>>" ">=>" ">>=" ">>-" ">-" "-<" "-<<" ">->" "<-<" "<-|" - "<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>" "~>" "~-" "-~" - "~@" "[||]" "|]" "[|" "|}" "{|" "[<" ">]" "|>" "<|" "||>" "<||" - "|||>" "<|||" "<|>" "..." ".." ".=" "..<" ".?" "::" ":::" ":=" "::=" - ":?" ":?>" "//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__" "???" - "<:<" ";;;")) - (global-ligature-mode t) -#+END_SRC - -Use variable-width fonts in text buffers. ~mixed-pitch-mode~ keeps most code blocks as monospace. - -#+BEGIN_SRC elisp - (add-hook 'text-mode-hook 'mixed-pitch-mode) - - (defun no-mixed-pitch () - (mixed-pitch-mode -1)) - - (add-hook 'yaml-ts-mode-hook 'no-mixed-pitch) - (add-hook 'mhtml-mode 'no-mixed-pitch) - (add-hook 'nxml-mode 'no-mixed-pitch) -#+END_SRC - -A basic ace-window setup. Allows switching between windows quickly (although avy can do the same...). - -#+BEGIN_SRC elisp - (bind-keys ("M-o" . ace-window) - ("s-o" . ace-window) - ("C-c o" . ace-window)) - (setopt aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) - (setopt aw-dispatch-always t) -#+END_SRC - -Set up a few project- / workspace-related key bindings. - -#+BEGIN_SRC elisp - (bind-keys ("s-f" . project-find-file) - ("s-F" . project-switch-project) - ;; ("s-s" . persp-switch) - ) - - (with-eval-after-load 'tab-bar - (bind-keys :map tab-bar-mode-map - ("s-s" . tab-bar-switch-to-tab))) -#+END_SRC - -#+RESULTS: -: tab-bar-switch-to-tab - -Don't run a server. I'm not using this, I can always turn it back on if I need to. - -#+BEGIN_SRC elisp - (server-mode -1) -#+END_SRC - -All the beeping is annoying. This flashes a huge icon over the screen, which is still annoying, but -less so? - -#+BEGIN_SRC elisp - (setopt visible-bell t) -#+END_SRC - -I'm trying this out. This disables visual selections. It's bold, which is why I like it :) - -#+BEGIN_SRC elisp - (transient-mark-mode -1) -#+END_SRC - -I use tabs to manage multiple "workspaces". I don't tie a project to a tab or anything; often fixing -a single bug might involve pulling in files from multiple projects. Rather, I open a file per bug / -fix / issue, and leave it open until I'm done with that issue. - -~tab-bar-history-mode~ allows quickly rolling back window changes; it can be used to undo a context -change when drilling into a help page or something. I've set up repeat mode to make it easier to -move backward / forward through history. - -#+BEGIN_SRC elisp - (bind-keys :repeat-map tab-bar-repeat-map - ("" . tab-bar-history-back) - ("" . tab-bar-history-forward)) - - (tab-bar-history-mode) -#+END_SRC - -Make compilation mode buffers auto-scroll to the first error: - -#+BEGIN_SRC elisp - (setopt compilation-scroll-output 'first-error) -#+END_SRC - -*** Window Placement -This section contains rules about where ~display-buffer~ should place windows. I guess I'm currently -going for an IDE-style layout, with one main editor window, and auxiliary bottom and right side -windows. I place windows in the bottom or on the right mostly depending on how wide I expect their -content to be. - -#+BEGIN_SRC elisp - (defun jf/window-children (window) - (let ((result nil) - (child (window-child window))) - (while child - (push child result) - (setq child (window-next-sibling child))) - (reverse result))) - - (defun jf/window-main-root (&optional frame) - "Return FRAME's main (non-side) window or window subtree." - (let* ((root (frame-root-window (or frame (selected-frame)))) - (children (jf/window-children root))) - (if (seq-some (lambda (w) (window-parameter w 'window-side)) children) - (seq-find (lambda (w) (not (window-parameter w 'window-side))) children) - root))) - - (defun jf/window-leaves (window) - "Return the live leaf windows in WINDOW's subtree." - (if (window-live-p window) - (list window) - (mapcan #'jf/window-leaves (jf/window-children window)))) - - (defun jf/topright-window (windows) - "Return whichever of WINDOWS sits furthest up, then furthest right." - (car (sort (copy-sequence windows) - (lambda (a b) - (let ((top-a (nth 1 (window-edges a))) - (top-b (nth 1 (window-edges b)))) - (if (= top-a top-b) - (> (nth 0 (window-edges a)) (nth 0 (window-edges b))) - (< top-a top-b))))))) - - (defun jf/display-buffer-right (buffer alist) - "Display BUFFER on the right of the main editing area. - - Applies three rules, based on the shape of the frame's non-side windows: - 1. One window -> split it and show BUFFER in the new right window. - 2. Two windows side-by-side -> show BUFFER in the right one. If the - right window is selected, swap the two windows first (keeping - focus on the buffer that was active) so BUFFER doesn't evict it. - 3. Any other layout -> show BUFFER in the upper-rightmost window." - (let ((main (jf/window-main-root))) - (cond - ((window-live-p main) - (let* ((new (split-window main nil 'right)) - (width (max 1 (- (frame-width) 120))) - (delta (window-resizable new (- width (window-width new)) t))) - (window-resize new delta t) - (window--display-buffer buffer new 'window alist))) - ((and (window-left-child main) - (= (length (jf/window-children main)) 2) - (seq-every-p #'window-live-p (jf/window-children main))) - (let* ((left (window-left-child main)) - (right (window-next-sibling left))) - (when (eq (selected-window) right) - (window-swap-states left right) - (select-window left)) - (window--display-buffer buffer right 'reuse alist))) - (t - (window--display-buffer buffer (jf/topright-window (jf/window-leaves main)) 'reuse alist))))) - - - (setopt popper-reference-buffers - '("\\*Flymake diagnostics .*" - "\\*Embark Export: .*" - "\\*Embark Collect .*" - "\\*xref\\*" - "\\*compilation\\*" - "\\*pnpm:.*" - "\\*sly-mrepl *" - "\\*lispy-message\\*" - "\\*occur\\*" - ".*eshell\\*" - "\\*SQL: .*" - "magit-revision: .*" - "\\*Help\\*" - "\\*rustfmt\\*" - "\\*slack: .*" - "\\*forge: .*" - "new-pullreq" - "\\*claude:.*" - "\\*Man .*" - "\\*List .*" - "\\*eldoc\\*")) - (setopt popper-display-control nil) - - (popper-mode) - (popper-echo-mode) - - (bind-keys ("C-q" . popper-toggle) - ("C-;" . popper-cycle)) - - (defvar jf/fullscreen-popup--param 'jf/fullscreen-saved-configuration - "Key used to stash the window configuration a fullscreen popup replaced. - Stored per-tab when `tab-bar-mode' is active, otherwise as a plain frame - parameter; see `jf/tab-or-frame-parameter'.") - - (defvar-local jf/fullscreen-popup-p nil - "Non-nil in a buffer currently being displayed as a fullscreen popup.") - - (defun jf/current-tab-cell (&optional frame) - "Return FRAME's current tab's raw data cell, or nil without `tab-bar-mode'." - (and (bound-and-true-p tab-bar-mode) - (assq 'current-tab (tab-bar-tabs frame)))) - - (defun jf/tab-or-frame-parameter (parameter &optional frame) - "Like `frame-parameter', but scoped to FRAME's selected tab when tabs are in use. - - A plain frame parameter is shared by every tab on that frame, which is wrong - for state like `jf/fullscreen-popup--param': switching tabs swaps in an - entirely different window layout, so \"the layout to restore\" has to travel - with the tab, not the frame." - (if-let ((tab (jf/current-tab-cell frame))) - (alist-get parameter tab) - (frame-parameter frame parameter))) - - (defun jf/set-tab-or-frame-parameter (parameter value &optional frame) - "Set PARAMETER to VALUE, scoped like `jf/tab-or-frame-parameter'." - (if-let ((tab (jf/current-tab-cell frame))) - (let ((cell (assq parameter tab))) - (if cell - (setcdr cell value) - (setcdr tab (cons (cons parameter value) (cdr tab))))) - (set-frame-parameter frame parameter value))) - - (defun jf/display-buffer-fullscreen (buffer alist) - "Display BUFFER, deleting other windows, saving the prior layout. - - Intended for use in `display-buffer-alist' for popups (e.g. Magit - status/log) that should take over the frame while displayed, but - restore the previous window configuration once closed via either - `q' or `popper-toggle' -- both end up calling `quit-window', which - is where the restore actually happens; see - `jf/quit-window-restore-fullscreen'." - (unless (jf/tab-or-frame-parameter jf/fullscreen-popup--param) - (jf/set-tab-or-frame-parameter jf/fullscreen-popup--param - (current-window-configuration))) - (when-let ((window (or (display-buffer-reuse-window buffer alist) - (display-buffer-same-window buffer alist) - (display-buffer-pop-up-window buffer alist) - (display-buffer-use-some-window buffer alist)))) - (let ((ignore-window-parameters t)) - (delete-other-windows window)) - (with-current-buffer buffer - (setq jf/fullscreen-popup-p t)) - window)) - - (defun jf/quit-window-restore-fullscreen (orig-fn &optional kill window) - "Restore the saved window configuration after quitting a fullscreen popup. - - Advice around `quit-window' so both `q' (which Magit and other - special-mode buffers bind to buffer-quitting commands that call - `quit-window') and Popper's close/toggle commands (which also call - `quit-window' directly) trigger the restore. Only restores once the - last fullscreen popup on the tab (or frame, without `tab-bar-mode') - has closed, so opening one fullscreen popup from within another - falls back to the outer one first." - (let* ((window (or window (selected-window))) - (was-fullscreen (buffer-local-value 'jf/fullscreen-popup-p - (window-buffer window))) - (frame (window-frame window))) - (funcall orig-fn kill window) - (when (and was-fullscreen - (not (and (window-live-p window) - (buffer-local-value 'jf/fullscreen-popup-p - (window-buffer window)))) - (jf/tab-or-frame-parameter jf/fullscreen-popup--param frame)) - (let ((conf (jf/tab-or-frame-parameter jf/fullscreen-popup--param frame))) - (jf/set-tab-or-frame-parameter jf/fullscreen-popup--param nil frame) - (set-window-configuration conf))))) - - (advice-add 'quit-window :around #'jf/quit-window-restore-fullscreen) - - (setopt split-width-threshold 200) - (setopt split-height-threshold nil) - (setopt display-buffer-base-action - '((display-buffer-reuse-window - display-buffer-reuse-mode-window - display-buffer-use-some-window))) - - (setq display-buffer-alist - '(((or "\\*Flymake diagnostics .*" - "\\*Embark Export: .*" - "\\*Embark Collect .*" - "\\*xref\\*" - "\\*compilation\\*" - "\\*pnpm:.*" - "\\*sly-mrepl *" - "\\*lispy-message\\*" - "\\*occur\\*" - ".*eshell\\*" - "\\*SQL: .*") - (display-buffer-in-side-window) - (side . bottom) - (slot . 0) - (window-height . 0.33)) - ((or "magit: *" - "magit-log*" - "\\*Messages\\*" - "\\*trace-output\\*" - "\\*EGLOT connections\\*" - "magit-revision: .*" - "\\*Help\\*" - "\\*rustfmt\\*" - "\\*slack: .*" - "\\*forge: .*" - "new-pullreq" - "\\*claude:.*" - "\\*Man .*" - "\\*List .*" - "\\*Embark Actions\\*" - "\\*eldoc\\*") - (display-buffer-reuse-window - display-buffer-reuse-mode-window - jf/display-buffer-right)))) - - (setopt window-persistent-parameters - (cl-list* '(no-delete-other-windows . writable) - '(no-other-window . writable) - window-persistent-parameters)) -#+END_SRC - - -** Editing -This configuration affects general text editing, across all modes. First, some packages: - -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - apheleia - avy - edit-indirect - embark - god-mode - # evil - # evil-collection - # evil-org - string-inflection - ]; - }; - } -#+END_SRC - -I find Emacs to be almost unusable on a normal keyboard unless there's some way to avoid all the -modifier keys. I tried Evil mode first. But although I love Vim, it just seemed to be hiding too -much of the Emacs native functionality. The keybindings were also much harder to configure. I tried -God mode, which has the benefit of re-using the normal Emacs keybindings and commands. It was -annoying, though, because some read-only buffers (like magit buffers) have their own "normal-mode" -commands that don't use modifiers. It felt jarring to switch to "insert mode" to use those -keys. It's also hard to repeat sequences of M-f, M-b, etc. I could have made a ton of repeat maps, -but I'm worried that having too many repeat maps will also be annoying (I'll have to explicitly -cancel them more frequently). In the end, I opted for Devil mode. It avoids modifier keys, and has -its own devil-mode-only repeat maps that don't interfere when using normal modifier key sequences. - - #+BEGIN_SRC elisp - ;; (setopt evil-want-keybinding nil) - ;; (require 'evil) - ;; (evil-mode 1) - ;; (require 'evil-collection) - ;; (evil-collection-init) - - ;; (require 'evil-org) - ;; (add-hook 'org-mode-hook 'evil-org-mode) - ;; (evil-org-set-key-theme '(navigation insert textobjects additional calendar)) - ;; (require 'evil-org-agenda) - ;; (evil-org-agenda-set-keys) - (require 'god-mode) - (require 'which-key) - - (bind-keys :map god-local-mode-map - ("i" . (lambda () (interactive) (god-mode-all -1))) - ("z" . repeat)) - (bind-keys ("" . (lambda () (interactive) (god-mode-all 1)))) - (defun my-god-mode-update-cursor-type () - (setq cursor-type (cond - ((and (featurep 'org-present) org-present-mode) nil) - ((or (and (featurep 'god-mode) god-local-mode) buffer-read-only) 'box) - (t 'bar)))) - - (add-hook 'post-command-hook 'my-god-mode-update-cursor-type) - (which-key-enable-god-mode-support) -#+END_SRC - -Avy allows jumping to any place on the screen with just a few keystrokes. I've stolen most of these -ideas from Karthinks' article [[https://karthinks.com/software/avy-can-do-anything/][Avy can do Anything]]. I need to find more uses for this, in line with -his article. - -#+BEGIN_SRC elisp - (defun avy-goto-char-timer-embark () - (interactive) - (require 'avy) - (let ((avy-action-oneshot - (lambda (pt) - (unwind-protect - (save-excursion - (goto-char pt) - (embark-act)) - (select-window - (cdr (ring-ref avy-ring 0))))))) - (avy-goto-char-timer))) - - (bind-keys - ("C-j" . avy-goto-char-timer) - ("M-j" . avy-goto-char-timer-embark) - ("C-." . embark-act)) - - (with-eval-after-load 'lispy - (bind-keys :map lispy-mode-map - ("C-j" . avy-goto-char-timer))) - - (with-eval-after-load 'org - (bind-keys :map org-mode-map - ("C-j" . avy-goto-char-timer))) -#+END_SRC - -Just "silently" wrap when searching at the bottom of a document. - -#+BEGIN_SRC elisp - (setopt isearch-wrap-pause 'no-ding) -#+END_SRC - -#+BEGIN_SRC elisp - (defun string-inflection-rust-style-cycle-function (str) - (cond - ((string-inflection-snake-case-p str) - (string-inflection-upcase-function str)) - ((string-inflection-upcase-p str) - (string-inflection-pascal-case-function str)) - (t - (string-inflection-snake-case-function str)))) - - (defun string-inflection-rust-style-cycle () - (interactive) - (require 'string-inflection) - (string-inflection--symbol-or-region #'string-inflection-rust-style-cycle-function)) - - (bind-keys :repeat-map string-inflection-java-map - ("u" . string-inflection-java-style-cycle)) - - (bind-keys :repeat-map string-inflection-rust-map - ("u" . string-inflection-rust-style-cycle)) - - (bind-keys ("C-x 4 n" . edit-indirect-region)) -#+END_SRC - -Some auto-formatting stuff - -#+begin_src elisp - (with-eval-after-load 'apheleia - (push '(prisma-format "prisma" "format" "--schema" filepath) apheleia-formatters)) -#+end_src - -** Workflow -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - buffer-terminator - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (buffer-terminator-mode 1) - - (bind-keys - ("s-b" . consult-project-buffer)) -#+END_SRC - -** Language Support -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - consult-eglot - graphql-ts-mode - haskell-ts-mode - jenkinsfile-mode - jq-mode - (lispy.overrideAttrs { - packageRequires = [ - ace-window iedit swiper zoutline hydra - ]; - ignoreCompilationError = true; - }) - # lispyville - lua-mode - nix-mode - pnpm-mode - prisma-ts-mode - rust-mode - sly - sly-macrostep - sql-indent - tree-sitter - (treesit-grammars.with-grammars (g: [ - g.tree-sitter-bash - g.tree-sitter-commonlisp - g.tree-sitter-css - g.tree-sitter-dockerfile - g.tree-sitter-dot - g.tree-sitter-elisp - g.tree-sitter-graphql - g.tree-sitter-gitcommit - g.tree-sitter-gitignore - g.tree-sitter-haskell - g.tree-sitter-html - g.tree-sitter-java - g.tree-sitter-javascript - g.tree-sitter-jq - g.tree-sitter-json - g.tree-sitter-json5 - g.tree-sitter-just - g.tree-sitter-ledger - g.tree-sitter-llvm - g.tree-sitter-make - g.tree-sitter-nginx - g.tree-sitter-nix - g.tree-sitter-org - g.tree-sitter-php - g.tree-sitter-prisma - g.tree-sitter-python - g.tree-sitter-rust - g.tree-sitter-rust-format-args - g.tree-sitter-scss - g.tree-sitter-sql - g.tree-sitter-toml - g.tree-sitter-tsx - g.tree-sitter-typescript - g.tree-sitter-xml - g.tree-sitter-yaml - ])) - yaml-pro - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (electric-pair-mode 1) - - (setq-default indent-tabs-mode nil) - (setq-default tab-width 4) - (global-tree-sitter-mode 1) - - (setopt ediff-split-window-function 'split-window-horizontally) - - (defun project-nix-flake () - (interactive) - (nix-flake (project-root (project-current t)))) - (bind-keys ("C-x p n" . project-nix-flake)) - #+END_SRC - -Show a vertical bar 100 characters out; ideally I don't want lines longer than this. 100 characters -is about the max you can have and still fit two files side-by-side on the screen. - -#+begin_src elisp - (setq-default fill-column 120) - - (defun jf-init-fill-column () - (display-fill-column-indicator-mode 1)) - (add-hook 'prog-mode-hook 'jf-init-fill-column) -#+end_src - -I had some weird problems with jsx code where the default M-q would just do a text fill rather than -a re-indent. The solution seems to be to only do the ~syntax-ppss~ stuff if tree-sitter isn't -available? It seems to work for now anyway... - -#+begin_src elisp - (with-eval-after-load 'prog-mode - (defun my-prog-fill-reindent-defun (&optional argument) - "Refill or reindent the paragraph or defun that contains point. - - If the point is in a string or a comment, fill the paragraph that - contains point or follows point. - - Otherwise, reindent the function definition that contains point - or follows point." - (interactive "P") - (save-excursion - (let ((treesit-text-node - (and (treesit-available-p) - (treesit-parser-list) - (treesit-node-match-p - (treesit-node-at (point)) 'text t)))) - (if (or treesit-text-node - (and (not (and (treesit-available-p) (treesit-parser-list))) (nth 8 (syntax-ppss))) - (re-search-forward "\\s-*\\s<" (line-end-position) t)) - (fill-paragraph argument (region-active-p)) - (beginning-of-defun) - (let ((start (point))) - (end-of-defun) - (indent-region start (point) nil)))))) - - (advice-add 'prog-fill-reindent-defun :override #'my-prog-fill-reindent-defun)) -#+end_src - -#+BEGIN_SRC elisp - (add-hook 'compilation-filter-hook 'ansi-color-compilation-filter) -#+END_SRC - -*** Flymake -#+begin_src elisp - (autoload 'flymake-show-project-diagnostics "flymake") - (autoload 'flymake-goto-next-error "flymake") - (autoload 'flymake-goto-prev-error "flymake") - - (with-eval-after-load 'prog-mode - (bind-keys :map prog-mode-map - ("C-c D" . flymake-show-project-diagnostics) - ("M-n" . flymake-goto-next-error) - ("M-p" . flymake-goto-prev-error))) - - (with-eval-after-load 'flymake - (bind-keys :map flymake-project-diagnostics-mode-map - ("q" . quit-window))) -#+end_src - -*** Eglot -I don't like UI elements that insert fake text into the file. They mess up the spacing in the file, and can make everything too wide. - -#+BEGIN_SRC elisp - (setopt eglot-ignored-server-capabilities '(:inlayHintProvider :semanticTokensProvider)) - (add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)) nil t) - (setopt eglot-code-action-indications nil) - (setopt eglot-events-buffer-config '(:size 300000 :format full)) -#+END_SRC - -Set up a few useful eglot keys. - -#+begin_src elisp - (with-eval-after-load 'eglot - (bind-keys :map eglot-mode-map - ("C-c a" . eglot-code-actions) - ("C-c r" . eglot-rename) - ("C-c i" . eglot-find-implementation))) -#+end_src - -Eglot's yasnippet support uses the tab key, but I *also* use the tab key both for indentation, and -for completion. That makes it really annoying to use; don't use yasnippet with eglot. - -#+begin_src elisp - (with-eval-after-load 'eglot - (add-to-list 'eglot-stay-out-of 'yasnippet)) -#+end_src - -*** Rust -#+BEGIN_SRC elisp - (setq major-mode-remap-alist '((rust-mode . rust-mode))) - - (setopt rust-mode-treesitter-derive t) - - (with-eval-after-load 'rust-prog-mode - (bind-keys :map rust-mode-map - ("C-c u" . string-inflection-rust-style-cycle))) -#+END_SRC - -*** YAML -#+BEGIN_SRC elisp - (defun yaml-validate () - (interactive) - (compile (concat "yamllint -d '{extends: default, rules: {indentation: {indent-sequences: false}, braces: {max-spaces-inside: 1}}}' " (buffer-file-name)) t)) - - (add-hook 'yaml-ts-mode-hook 'yaml-pro-ts-mode) -#+END_SRC - -*** Lisp -#+BEGIN_SRC elisp - (setopt inferior-lisp-program "sbcl") - - (setopt lispy-compat '(god-mode edebug)) - (add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1))) - (add-hook 'lisp-mode-hook (lambda () (lispy-mode 1))) - (add-hook 'lisp-data-mode-hook (lambda () (lispy-mode 1))) - (with-eval-after-load 'lispy - ;; (require 'lispyville) - ;; (add-hook 'lispy-mode-hook #'lispyville-mode) - (bind-keys :map lispy-mode-map - ("i" . (lambda () - (interactive) - (if (and (featurep 'god-mode) god-global-mode) - (god-mode-all -1) - (special-lispy-tab)))))) -#+END_SRC - -*** Typescript -#+BEGIN_SRC elisp - (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode)) - (add-to-list 'major-mode-remap-alist '(typescript-mode . typescript-ts-mode)) - (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-mode)) - (add-to-list 'major-mode-remap-alist '(tsx-mode . tsx-ts-mode)) - (setopt typescript-ts-mode-indent-offset 2) - (with-eval-after-load 'typescript-ts-mode - (defun my-tsx-ts-mode--indent-compatibility-b893426 () - "Indent rules helper, to handle different releases of tree-sitter-tsx. - Check if a node type is available, then return the right indent rules." - ;; handle https://github.com/tree-sitter/tree-sitter-typescript/commit/b893426b82492e59388a326b824a346d829487e8 - (condition-case nil - (progn (treesit-query-capture 'tsx '((jsx_fragment) @capture)) - `(((match "<" "jsx_fragment") parent 0) - ((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset))) - (treesit-query-error - `(((match "<" "jsx_text") parent 0) - ((parent-is "jsx_text") grand-parent typescript-ts-mode-indent-offset))))) - (advice-add 'tsx-ts-mode--indent-compatibility-b893426 :override #'my-tsx-ts-mode--indent-compatibility-b893426)) - - (defvar my/edit-indirect-tag-modes - '(("graphql" . graphql-ts-mode) - ("gql" . graphql-ts-mode) - ("sql" . sql-mode) - ("css" . css-mode)) - "Map of tagged-template tag names to major modes.") - - (defun my/edit-indirect--node-bounds (node) - "Return (BEG . END) for NODE's contents, excluding delimiters." - (cons (1+ (treesit-node-start node)) - (1- (treesit-node-end node)))) - - (defun my/edit-indirect-tagged-template-at-point () - (interactive) - (let* ((node (treesit-node-at (point))) - (tmpl-node (treesit-parent-until - node - (lambda (n) (equal (treesit-node-type n) "template_string")))) - (str-node (unless tmpl-node - (treesit-parent-until - node - (lambda (n) (equal (treesit-node-type n) "string"))))) - (target-node (or tmpl-node str-node))) - (unless target-node - (user-error "Point is not inside a string or template literal")) - (let* ((tagged-node (and tmpl-node (treesit-node-parent tmpl-node))) - (tag-node (and tagged-node - (equal (treesit-node-type tagged-node) "call_expression") - (treesit-node-child-by-field-name tagged-node "function"))) - (tag-name (and tag-node (treesit-node-text tag-node t))) - (mode (cond - (tag-name (or (cdr (assoc tag-name my/edit-indirect-tag-modes)) - major-mode)) - (t 'text-mode))) - (bounds (my/edit-indirect--node-bounds target-node)) - (buf (edit-indirect-region (car bounds) (cdr bounds) t))) - (with-current-buffer buf - (funcall mode))))) - - (with-eval-after-load 'typescript-ts-mode - (define-key typescript-ts-mode-map (kbd "C-c '") #'my/edit-indirect-tagged-template-at-point) - (define-key tsx-ts-mode-map (kbd "C-c '") #'my/edit-indirect-tagged-template-at-point)) - - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs '((prisma-ts-mode) "prisma-language-server" "--stdio"))) -#+END_SRC - -*** Java -#+BEGIN_SRC elisp - (push '(java-mode . java-ts-mode) major-mode-remap-alist) - (with-eval-after-load 'java-ts-mode - (bind-keys :map java-ts-mode-map - ("C-c u" . string-inflection-java-style-cycle)) - (push `((n-p-gp nil "block" "lambda_expression") parent-bol java-ts-mode-indent-offset) - (cdar java-ts-mode--indent-rules)) - (push `((n-p-gp "}" "block" "lambda_expression") parent-bol 0) - (cdar java-ts-mode--indent-rules)) - - (add-hook 'java-mode-hook (lambda () (c-set-offset 'arglist-intro '+))) - - (defvar jf/class-name-to-file-search-path (list "src/main/java" "src/test/java")) - - (defun jf/class-name-to-file (class-name) - (let ((root (project-root (project-current t))) - (fragment (format "%s.java" (replace-regexp-in-string "\\." "/" class-name))) - (result)) - (dolist (p jf/class-name-to-file-search-path result) - (unless result - (let ((path (format "%s/%s" p fragment))) - (when (file-exists-p (concat root path)) - (setq result path))))))) - - (defun jf/compile-class-to-file () - (jf/class-name-to-file (concat (match-string 1) (match-string 2))))) - - ;(add-to-list 'compilation-error-regexp-alist-alist - ; '(java-stack-trace . - ; ("^[[:space:]]*at \\(\\(?:[[:lower:]]+\\.\\)+\\)[^(]+(\\([[:alnum:]]+\\)\\.java:\\([[:digit:]]+\\))" - ; jf/compile-class-to-file 3))) - ;(add-to-list 'compilation-error-regexp-alist 'java-stack-trace) -#+END_SRC - -*** SQL -#+BEGIN_SRC elisp - (add-hook 'sql-mode-hook 'sqlind-minor-mode) -#+END_SRC - -*** Haskell -#+BEGIN_SRC elisp - (setopt haskell-ts-use-indent t) -#+END_SRC - -** Envrc -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - envrc - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (add-hook 'after-init-hook 'envrc-global-mode) - (setopt envrc-show-summary-in-minibuffer nil) -#+END_SRC - -** Magit -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - consult-gh - diff-hl - forge - git-timemachine - magit - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (setopt magit-define-global-key-bindings 'recommended) - (bind-keys ("s-g" . magit)) - - (setopt magit-save-repository-buffers 'dontask) - (setopt magit-commit-show-diff nil) - (with-eval-after-load 'magit - (set-face-attribute 'git-commit-summary nil :inherit nil) - (defun magit-fetch-all-on-load () - (magit-run-git-async "fetch" "--all")) - (add-hook 'magit-status-mode-hook 'magit-fetch-all-on-load) - (remove-hook 'magit-status-headers-hook 'magit-insert-tags-header) - (require 'forge) - (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)) - - (with-eval-after-load 'forge - (add-hook 'forge-edit-post-hook 'forge-create-pullreq-insert-single-commit-message) - (setopt forge-status-buffer-default-topic-filters - (forge--topics-spec :type 'topic :active nil :state 'open :order 'newest))) - - (global-diff-hl-mode) -#+END_SRC - -** Vertico -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - cape - consult - corfu - embark-consult - marginalia - orderless - vertico - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (bind-keys - ;; ("C-c M-x" . consult-mode-command) - ("C-c h" . consult-history) - ;; ("C-c k" . consult-kmacro) - ;; ("C-c m" . consult-man) - ;; ([remap Info-search] . consult-info) - - ;; ("C-x M-:" . consult-complex-command) - ("C-x b" . consult-buffer) - ("s-B" . consult-buffer) - ("C-x 4 b" . consult-buffer-other-window) - ("C-x 5 b" . consult-buffer-other-frame) - ;; ("C-x t b" . consult-buffer-other-tab) - ("C-x r b" . consult-bookmark) - ("C-x p b" . consult-project-buffer) - - ("M-#" . consult-register-load) - ("M-'" . consult-register-store) - ("C-M-#" . consult-register) - ("M-y" . consult-yank-pop) - ("M-g e" . consult-compile-error) - ("M-g f" . consult-flymake) - ("M-g g" . consult-goto-line) - ("M-g M-g" . consult-goto-line) - ("M-g o" . consult-org-heading) - ("M-g m" . consult-mark) - ("M-g k" . consult-global-mark) - ("M-g i" . consult-imenu) - ("M-g I" . consult-imenu-multi) - ("M-s r" . consult-ripgrep) - ("M-s l" . consult-line) - ("M-s L" . consult-line-multi) - ("M-s e" . consult-isearch-history) - :map isearch-mode-map - ("M-e" . consult-isearch-history) - ("M-s e" . consult-isearch-history) - ("M-s l" . consult-line) - ("M-s L" . consult-line-multi) - :map minibuffer-local-map - ("M-s" . consult-history) - ("M-r" . consult-history)) - - (advice-add 'register-preview :override #'consult-register-window) - (setopt register-preview-delay 0.5) - - (setopt xref-show-xrefs-function 'consult-xref) - (setopt xref-show-definitions-function 'consult-xref) - - (eval-when-compile - (require 'consult)) - - (with-eval-after-load 'consult - (consult-customize - consult-theme :preview-key '(:debounce 0.2 any) - consult-ripgrep consult-git-grep consult-grep consult-man - consult-bookmark consult-recent-file consult-xref - consult-source-bookmark consult-source-file-register - consult-source-recent-file - consult-source-buffer - ;; :preview-key "M-." - consult-source-project-recent-file - :preview-key '(:debounce 0.4 any))) - - (setopt consult-narrow-key "<") ;; "C-+" - - (use-package vertico - :custom - (vertico-count 25) - :config - (vertico-mode)) - - (setopt completion-styles '(orderless basic)) - - (use-package marginalia - :config - (marginalia-mode)) - - (use-package corfu - :custom - (corfu-cycle t) - (corfu-popupinfo-mode nil) - (corfu-quit-at-boundary nil) - (corfu-on-exact-match 'show) - :config - (global-corfu-mode)) - ;; (setq completion-in-region-function #'consult-completion-in-region) - - (use-package cape - :custom - (add-hook 'completion-at-point-functions 'cape-file)) -#+END_SRC - -** Org -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - org-modern - org-present - org-roam - verb - visual-fill-column - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (use-package org - :bind (("C-c A" . org-agenda) - ("C-c C" . org-capture) - ("C-c l" . org-id-store-link)) - :custom - (org-babel-load-languages '((emacs-lisp . t) (sql . t))) - (org-confirm-babel-evaluate nil) - (org-link-keep-stored-after-insertion t) - (org-capture-templates - '(("t" "Todo" entry - (here) - "** TODO %?\n:LOGBOOK:\n- State \"TODO\" %U\n:END:\n") - ("w" "Work stuff") - ("wp" "Work Project" entry - (file+headline "~/org/todo/work.org.age" "Next Actions") - "*** PROJ %?\n- State \"PROJ\" %U\n") - ("wa" "Work Next Action" entry - (file+headline "~/org/todo/work.org.age" "Next Actions") - "*** TODO %?\n- State \"TODO\" %U\n") - ("p" "Personal stuff") - ("pp" "Personal Project" entry - (file+headline "~/org/todo/personal.org.age" "Next Actions") - "*** PROJ %?\n- State \"PROJ\" %U\n") - ("pa" "Personal Next Action" entry - (file+headline "~/org/todo/personal.org.age" "Next Actions") - "*** TODO %?\n- State \"TODO\" %U\n"))) - (org-agenda-files '("~/org/todo/work.org" "~/org/todo/personal.org.age")) - (org-agenda-todo-ignore-scheduled 'future) - (org-agenda-tags-todo-honor-ignore-options t) - (org-enforce-todo-dependencies t) - (org-agenda-dim-blocked-tasks 'invisible) - :config - (add-hook 'org-mode-hook 'visual-line-mode) - (set-face-attribute 'org-level-1 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-2 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-3 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-4 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-5 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-6 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-7 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-8 nil :font "Iosevka Aile")) - - (defvar-local my/org-present-face-remapping-cookies nil) - (use-package org-present - :config - (require 'face-remap) - (require 'visual-fill-column) - - (defun my/org-present-start () - (setq-local my/org-present-face-remapping-cookies nil) - ;; Tweak font sizes - (dolist (face '((default :height 2.0) - (header-line :height 4.0 :background "#303446") - (org-document-title :height 1.75) - (org-code :height 2.0) - (org-verbatim :height 2.0) - (org-block :height 1.25) - (org-block-begin-line :height 0.7) - (org-level-1 :height 3.0) - (org-level-2 :height 3.0) - (org-level-3 :height 3.0) - (org-level-4 :height 3.0) - (org-level-5 :height 3.0) - (org-level-6 :height 3.0) - (org-level-7 :height 3.0) - (org-level-8 :height 3.0))) - (push (apply #'face-remap-add-relative face) my/org-present-face-remapping-cookies)) - - ;; Set a blank header line string to create blank space at the top - (setq header-line-format " ") - - ;; Display inline images automatically - (org-display-inline-images) - (org-present-hide-cursor) - - ;; Center the presentation and wrap lines - (visual-fill-column-mode 1) - (setq visual-fill-column-center-text t) - ;; (visual-line-mode 1) - ) - - (defun my/org-present-end () - ;; Reset font customizations - (dolist (cookie my/org-present-face-remapping-cookies) - (face-remap-remove-relative cookie)) - - ;; Clear the header line string so that it isn't displayed - (setq header-line-format nil) - - ;; Stop displaying inline images - (org-remove-inline-images) - (org-present-show-cursor) - - ;; Stop centering the document - (visual-fill-column-mode 0) - ;; (visual-line-mode 0) - ) - - (add-hook 'org-present-mode-hook 'my/org-present-start) - (add-hook 'org-present-mode-quit-hook 'my/org-present-end)) - - ;; (require 'org-roam) - ;; (bind-keys ("C-c n l" . org-roam-buffer-toggle) - ;; ("C-c n f" . org-roam-node-find) - ;; ("C-c n g" . org-roam-graph) - ;; ("C-c n i" . org-roam-node-insert) - ;; ("C-c n c" . org-roam-capture)) - - ;; (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag))) - ;; (org-roam-db-autosync-mode) - - (use-package org-modern - :hook org-mode) - - ;; (define-key org-mode-map (kbd "C-c C-r") verb-command-map) -#+END_SRC - -** Terminal -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - eat - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (use-package eat - :commands (eat) - :hook (eshell-load-hook . eat-eshell-mode)) -#+END_SRC - -** Slack -#+BEGIN_SRC nix - (mkIf (cfg.redbook.enable && cfg.desktop.enable) { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - slack - ox-slack - ]; - extraConfig = builtins.readFile ./slack.el; - }; - }) -#+END_SRC - -#+BEGIN_SRC elisp :tangle modules/home/home/slack.el :noweb-ref nil - (use-package lui - :defer t - :custom - (lui-time-stamp-format "[%Y-%m-%d %H:%M]")) - - (use-package slack - :commands slack-start - :custom - (slack-render-profile-images-p nil) - (slack-buffer-function #'switch-to-buffer)) -#+END_SRC - -** Ledger -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - ledger-mode - ]; - }; - } -#+END_SRC - -** Age -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - age - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (use-package age - :custom - (age-default-identity "~/source/dotfiles/keys/yubi1age.txt") - (age-default-recipient "~/source/dotfiles/keys/myself.txt") - :config - (setenv "PINENTRY_PROGRAM" "pinentry-mac") - (age-file-enable)) -#+END_SRC - -** Claude Code -#+BEGIN_SRC nix - { - programs.claude-code = { - enable = true; - }; - - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - (epkgs.trivialBuild { - pname = "claude-code"; - version = "0.4.5"; - src = pkgs.fetchFromGitHub { - owner = "stevemolitor"; - repo = "claude-code.el"; - rev = "03199df8b3a1e9cd4857f0851f7a912ba524aff3"; - sha256 = "sha256-5QJrWIu4EgnHcOqMwlrs2JBBx7aI9OaSJswesr6Apfk="; - }; - packageRequires = [ epkgs.inheritenv ]; - }) - ]; - }; - } -#+END_SRC - -#+BEGIN_SRC elisp - (use-package claude-code - :bind-keymap ("C-c c" . claude-code-command-map) - :custom (claude-code-optimize-window-resize nil)) -#+END_SRC -* Theme -:PROPERTIES: -:header-args:nix: :noweb-ref nix-configs -:END: - -I like Catppuccin Frappe as my theme. Let's use it in as many places as we can. - -** Bat -#+BEGIN_SRC nix - { - programs.bat = { - config.theme = "catppuccin-frappe"; - themes = { - catppuccin-frappe = { - src = pkgs.fetchFromGitHub { - owner = "catppuccin"; - repo = "sublime-text"; # Bat uses sublime syntax for its themes - rev = "3d8625d937d89869476e94bc100192aa220ce44a"; - sha256 = "3ABUsfJpb6RO6AiuuSL5gwDofJIwC5tlEMzBrlY9/s0="; - }; - file = "Frappe.tmTheme"; - }; - }; - }; - } -#+END_SRC - -** Helix -#+BEGIN_SRC nix - { - programs.helix.settings.theme = "catppuccin_frappe"; - } -#+END_SRC - -** Tmux -#+BEGIN_SRC nix - { - programs.tmux.plugins = [ - { - plugin = pkgs.tmuxPlugins.catppuccin; - extraConfig = ''set -g @catppuccin_window_tabs_enabled on - set -g @catppuccin_window_default_text "#W" - set -g @catppuccin_window_current_text "#W"''; - } - ]; - } -#+END_SRC - -** Lazygit -#+BEGIN_SRC nix - { - xdg = { - enable = false -; - # configFile."lazygit/config.yml" = { - # enable = true; - # source = pkgs.fetchFromGitHub - # { - # owner = "catppuccin"; - # repo = "lazygit"; - # rev = "b2ecb6d41b6f54a82104879573c538e8bdaeb0bf"; # Replace with the latest commit hash - # sha256 = "9BBmWRcjNaJE9T0RKVEJaSnkrbMom0CLYE8PzAT6yFw="; - # } + /themes/frappe.yml; - # }; - }; - } -#+END_SRC - -** Starship -#+BEGIN_SRC nix - { - programs.starship.settings = builtins.fromTOML (builtins.readFile - (pkgs.fetchFromGitHub - { - owner = "catppuccin"; - repo = "starship"; - rev = "3e3e54410c3189053f4da7a7043261361a1ed1bc"; # Replace with the latest commit hash - sha256 = "soEBVlq3ULeiZFAdQYMRFuswIIhI9bclIU8WXjxd7oY="; - } + /palettes/frappe.toml)); - } -#+END_SRC - -** Wezterm -:PROPERTIES: -:header-args:lua: :noweb-ref wezterm-config -:END: - -#+BEGIN_SRC lua - config.color_scheme = 'Catppuccin Frappe' -#+END_SRC - -** Ghostty -#+BEGIN_SRC nix - { - programs.ghostty.settings.theme = "Catppuccin Frappe"; - } -#+END_SRC - -** Emacs -:PROPERTIES: -:header-args:elisp: :tangle modules/home/emacs.el -:END: - -#+BEGIN_SRC nix - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - catppuccin-theme - ]; - extraConfig = ''(defvar my-base-font-size ${if pkgs.stdenv.isDarwin then "130" else "100"}) - (require 'catppuccin-theme) - (setopt catppuccin-flavor 'frappe) - (load-theme 'catppuccin :no-confirm) - - ;; (set-face-attribute 'default nil :font "JetBrainsMono Nerd Font" :height 130) - ;; (set-face-attribute 'fixed-pitch nil :font "JetBrainsMono Nerd Font" :height 130) - (set-face-attribute 'default nil :font "Iosevka Term SS14" :height my-base-font-size) - (set-face-attribute 'fixed-pitch nil :font "Iosevka Term SS14" :height my-base-font-size) - (set-face-attribute 'variable-pitch nil :font "Iosevka Etoile" :height my-base-font-size) - ''; - }; - } -#+END_SRC diff --git a/modules/home/default.nix b/modules/home/default.nix index 9d7c8da..6c75d2c 100644 --- a/modules/home/default.nix +++ b/modules/home/default.nix @@ -1,23 +1,46 @@ -{lib, pkgs, config, ...}: let +{ + lib, + pkgs, + config, + ... +}: +let inherit (lib) mkIf mkMerge - ; + ; cfg = config.jfrikker; - in { - options.jfrikker = { - enable = lib.mkEnableOption "Joe Frikker's configuration"; - desktop.enable = lib.mkEnableOption "Desktop (GUI) applications"; - redbook.enable = lib.mkEnableOption "Redbook-specific configuration"; - - - }; - - config = mkIf cfg.enable (mkMerge [ - { - home.packages = with pkgs; [ - rlwrap - (sbcl.withPackages (ps: with ps; [ +in +{ + imports = [ + ./emacs.nix + ./helix.nix + ./starship.nix + ./tmux.nix + ]; + + options.jfrikker = { + enable = lib.mkEnableOption "Joe Frikker's configuration"; + desktop.enable = lib.mkEnableOption "Desktop (GUI) applications"; + redbook.enable = lib.mkEnableOption "Redbook-specific configuration"; + }; + + config = mkIf cfg.enable (mkMerge [ + { + home.packages = with pkgs; [ + age-plugin-yubikey + dust + fd + ledger + magic-wormhole + nil + nixfmt + openssh + rage + ripgrep + rlwrap + (sbcl.withPackages ( + ps: with ps; [ alexandria cl-markup cl-mock @@ -50,268 +73,147 @@ trivial-open-browser trivial-timeout uuid - ])) - ]; - } - { - home.packages = with pkgs; [ - rage - age-plugin-yubikey - yubikey-manager - ]; - } - { - home.packages = with pkgs; [ - dust - fd - ripgrep - tokei - xh - ]; - - programs.eza = { + ] + )) + tokei + xh + yubikey-manager + ]; + + programs = { + bash.enable = true; + gh.enable = true; + home-manager.enable = true; + jq.enable = true; + lazygit.enable = true; + nix-index.enable = true; + nix-index-database.comma.enable = true; + zoxide.enable = true; + + bat = { + enable = true; + config.style = "numbers"; + config.theme = "catppuccin-frappe"; + themes = { + catppuccin-frappe = { + src = pkgs.fetchFromGitHub { + owner = "catppuccin"; + repo = "sublime-text"; # Bat uses sublime syntax for its themes + rev = "3d8625d937d89869476e94bc100192aa220ce44a"; + sha256 = "3ABUsfJpb6RO6AiuuSL5gwDofJIwC5tlEMzBrlY9/s0="; + }; + file = "Frappe.tmTheme"; + }; + }; + }; + + claude-code = { + enable = true; + }; + + direnv = { + enable = true; + nix-direnv.enable = true; + }; + + eza = { enable = true; enableBashIntegration = true; enableZshIntegration = true; icons = "auto"; }; - } - { - home.packages = with pkgs; [ - ledger - ]; - } - { - home.packages = with pkgs; [ - nil - nixfmt - ]; - } - { - home.packages = with pkgs; [ - magic-wormhole - ]; - } - (mkIf cfg.redbook.enable { - home.packages = with pkgs; [ - openssh - ]; - }) - (mkIf cfg.desktop.enable { - home.packages = with pkgs; [ - google-chrome - nerd-fonts.jetbrains-mono - nerd-fonts.symbols-only - (iosevka-bin.override { variant = "Aile"; }) - (iosevka-bin.override { variant = "Etoile"; }) - (iosevka-bin.override { variant = "SGr-IosevkaTermSS14"; }) - nerd-fonts.symbols-only - keymapp - proton-pass - wireshark - ]; - - fonts.fontconfig.enable = true; - }) - (mkIf (cfg.redbook.enable && cfg.desktop.enable) { - home.packages = with pkgs; [ - linear - notion-app - (slack.overrideAttrs { - version = "4.50.143"; - src = pkgs.fetchurl { - url = "https://downloads.slack-edge.com/desktop-releases/mac/arm64/4.50.143/Slack-4.50.143-macOS.dmg"; - hash = "sha256-f5oYi8NPKXWpDcowFYMUjptbQs4V9hK2bm1TuaQeJn0="; - }; - }) - teams - ]; - }) - { - programs = { - bash.enable = true; - gh.enable = true; - home-manager.enable = true; - jq.enable = true; - lazygit.enable = true; - zoxide.enable = true; - }; - } - { - programs.bat = { - enable = true; - config.style = "numbers"; - }; - } - { - programs.nix-index.enable = true; - programs.nix-index-database.comma.enable = true; - } - { - programs.direnv = { - enable = true; - nix-direnv.enable = true; - }; - } - { - programs.fzf = { + + fzf = { defaultCommand = "fd --type f"; fileWidgetCommand = "fd --type f"; - fileWidgetOptions = ["--preview" "'bat --color=always --style=numbers --line-range=:500 {}'"]; + fileWidgetOptions = [ + "--preview" + "'bat --color=always --style=numbers --line-range=:500 {}'" + ]; changeDirWidgetCommand = "fd --type d"; tmux.enableShellIntegration = true; }; - } - { - programs.git = { + + git = { enable = true; settings.user.name = "Joe Frikker"; settings.user.email = "joe@redbookcattle.com"; settings.github.user = "joe@redbookcattle.com"; settings.merge.conflictstyle = "zdiff3"; }; - - programs.delta = { + + nh = { + enable = true; + }; + + delta = { enable = true; enableGitIntegration = true; }; - } - { - programs.helix = { + + zsh = { enable = true; - defaultEditor = true; - - settings.editor = { - auto-format = false; - auto-pairs = false; - auto-save.after-delay.enable = true; - auto-save.after-delay.timeout = 1000; - cursorline = true; - idle-timeout = 0; - rulers = [120]; - text-width = 100; - lsp.goto-reference-include-declaration = false; - cursor-shape.insert = "bar"; - smart-tab.supersede-menu = true; - whitespace.render.newline = "all"; - }; - - settings.keys.normal.space = { - space = "file_picker"; - "," = "buffer_picker"; - # b = "@:sh wezterm cli spawn --cwd . -- tig blame %"; - }; - - - languages.language-server.eslint = { - args = ["--stdio"]; - command = "vscode-eslint-language-server"; - config.nodePath = ""; - config.quiet = false; - config.rulesCustomizations = []; - config.run = "onType"; - config.validate = "on"; - config.codeAction.disableRuleComment.enable = true; - config.codeAction.disableRuleComment.location = "separateLine"; - config.codeAction.showDocumentation.enable = true; - config.problems.shortenToSingleLine = false; - }; - - languages.language-server.jdtls.config.java = { - autobuild.enabled = true; - completion.maxResults = 1000; - format.settings.url = "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml"; - }; - - languages.language-server.rust-analyzer.config.files.watcher = "client"; - - languages.language = [ + oh-my-zsh.enable = true; + syntaxHighlighting.enable = true; + autosuggestion.enable = true; + historySubstringSearch.enable = true; + history.share = false; + cdpath = [ "~/source" ]; + plugins = [ { - name = "typescript"; - language-servers = [ "typescript-language-server" "vscode-eslint-language-server" ]; - indent.tab-width = 4; - indent.unit = " "; - } - { - name = "tsx"; - language-servers = [ "typescript-language-server" "vscode-eslint-language-server" ]; - indent.tab-width = 4; - indent.unit = " "; - } - { - name = "java"; - indent.tab-width = 4; - indent.unit = " "; + name = "zsh-nix-shell"; + file = "nix-shell.plugin.zsh"; + src = pkgs.fetchFromGitHub { + owner = "chisui"; + repo = "zsh-nix-shell"; + rev = "v0.7.0"; + sha256 = "149zh2rm59blr2q458a5irkfh82y3dwdich60s9670kl3cl5h2m1"; + }; } ]; }; - } - { - programs.nh = { - enable = true; - }; - } - { - programs.starship = { - enable = true; + }; + xdg = { + enable = false; + # configFile."lazygit/config.yml" = { + # enable = true; + # source = pkgs.fetchFromGitHub + # { + # owner = "catppuccin"; + # repo = "lazygit"; + # rev = "b2ecb6d41b6f54a82104879573c538e8bdaeb0bf"; # Replace with the latest commit hash + # sha256 = "9BBmWRcjNaJE9T0RKVEJaSnkrbMom0CLYE8PzAT6yFw="; + # } + /themes/frappe.yml; + # }; + }; + } + (mkIf cfg.desktop.enable { + home.packages = with pkgs; [ + google-chrome + nerd-fonts.jetbrains-mono + nerd-fonts.symbols-only + (iosevka-bin.override { variant = "Aile"; }) + (iosevka-bin.override { variant = "Etoile"; }) + (iosevka-bin.override { variant = "SGr-IosevkaTermSS14"; }) + nerd-fonts.symbols-only + keymapp + proton-pass + wireshark + ]; + + programs = { + ghostty = { + enable = false; + package = pkgs.ghostty-bin; settings = { - add_newline = false; - container.disabled = true; - git_branch.symbol = " "; - git_status.ahead = "⇡\${count}"; - git_status.behind = "⇣\${count}"; - git_status.diverged = "⇕⇡\${ahead_count}⇣\${behind_count}"; - git_status.format = "([\\[\$conflicted\$staged\$ahead_behind\\]](\$style) )"; - java.symbol = " "; - line_break.disabled = true; - nix_shell.symbol = " "; - nodejs.symbol = " "; - package.disabled = true; - palette = "catppuccin_frappe"; - python.symbol = " "; - package.symbol = " "; - rust.symbol = " "; - docker_context.disabled = true; - shell.disabled = false; + adjust-cell-height = "15%"; + font-family = "Iosevka Term SS14"; + font-size = 13; + theme = "Catppuccin Frappe"; }; }; - } - { - programs.tmux = { - baseIndex = 1; - clock24 = true; - customPaneNavigationAndResize = true; - historyLimit = 50000; - enable = true; - escapeTime = 0; - keyMode = "vi"; - mouse = true; - shell = pkgs.zsh + "/bin/zsh"; - shortcut = "Space"; - tmuxinator.enable = true; - terminal = "xterm-256color"; - extraConfig = '' - bind-key -T copy-mode-vi 'v' send -X begin-selection - bind-key -T copy-mode-vi 'y' send -X copy-selection-and-cancel - - bind-key -T launcher g popup -h 100% -w 100% -E lazygit - bind-key g switch-client -Tlauncher - set -g default-command "" - ''; - }; - } - (mkIf cfg.desktop.enable { - programs.wezterm = { - enable = true; - extraConfig = '' - local config = {} - config.default_prog = { '${pkgs.zsh}/bin/zsh', '-l'} - config.font_size = ${if pkgs.stdenv.isDarwin then "13.0" else "10.0"} - '' + builtins.readFile ./wezterm.lua; - }; - }) - (mkIf cfg.desktop.enable { - programs.kitty = { + + kitty = { enable = true; font = { name = "Iosevka Term SS14"; @@ -331,316 +233,43 @@ "ctrl+9" = "goto_tab 9"; }; }; - }) - { - programs.ghostty = { - enable = false; - package = pkgs.ghostty-bin; - settings = { - adjust-cell-height = "15%"; - font-family = "Iosevka Term SS14"; - font-size = 13; + + wezterm = { + enable = true; + extraConfig = '' + local config = {} + config.default_prog = { '${pkgs.zsh}/bin/zsh', '-l'} + config.font_size = ${if pkgs.stdenv.isDarwin then "13.0" else "10.0"} + '' + + builtins.readFile ./wezterm.lua; + }; + }; + + fonts.fontconfig.enable = true; + }) + (mkIf (cfg.redbook.enable && cfg.desktop.enable) { + home.packages = with pkgs; [ + linear + notion-app + (slack.overrideAttrs { + version = "4.50.143"; + src = pkgs.fetchurl { + url = "https://downloads.slack-edge.com/desktop-releases/mac/arm64/4.50.143/Slack-4.50.143-macOS.dmg"; + hash = "sha256-f5oYi8NPKXWpDcowFYMUjptbQs4V9hK2bm1TuaQeJn0="; }; - }; - } - { - programs.zsh = { - enable = true; - oh-my-zsh.enable = true; - syntaxHighlighting.enable = true; - autosuggestion.enable = true; - historySubstringSearch.enable = true; - history.share = false; - cdpath = ["~/source"]; - }; - } - { - programs.zsh.plugins = [ - { - name = "zsh-nix-shell"; - file = "nix-shell.plugin.zsh"; - src = pkgs.fetchFromGitHub { - owner = "chisui"; - repo = "zsh-nix-shell"; - rev = "v0.7.0"; - sha256 = "149zh2rm59blr2q458a5irkfh82y3dwdich60s9670kl3cl5h2m1"; - }; - } - ]; - } - (mkIf cfg.desktop.enable { - programs.emacs = { - enable = true; - extraConfig = builtins.readFile ./emacs.el; - }; - }) - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - ace-window - doom-modeline - free-keys - ligature - mixed-pitch - popper - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - apheleia - avy - edit-indirect - embark - god-mode - # evil - # evil-collection - # evil-org - string-inflection - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - buffer-terminator - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - consult-eglot - graphql-ts-mode - haskell-ts-mode - jenkinsfile-mode - jq-mode - (lispy.overrideAttrs { - packageRequires = [ - ace-window iedit swiper zoutline hydra - ]; - ignoreCompilationError = true; - }) - # lispyville - lua-mode - nix-mode - pnpm-mode - prisma-ts-mode - rust-mode - sly - sly-macrostep - sql-indent - tree-sitter - (treesit-grammars.with-grammars (g: [ - g.tree-sitter-bash - g.tree-sitter-commonlisp - g.tree-sitter-css - g.tree-sitter-dockerfile - g.tree-sitter-dot - g.tree-sitter-elisp - g.tree-sitter-graphql - g.tree-sitter-gitcommit - g.tree-sitter-gitignore - g.tree-sitter-haskell - g.tree-sitter-html - g.tree-sitter-java - g.tree-sitter-javascript - g.tree-sitter-jq - g.tree-sitter-json - g.tree-sitter-json5 - g.tree-sitter-just - g.tree-sitter-ledger - g.tree-sitter-llvm - g.tree-sitter-make - g.tree-sitter-nginx - g.tree-sitter-nix - g.tree-sitter-org - g.tree-sitter-php - g.tree-sitter-prisma - g.tree-sitter-python - g.tree-sitter-rust - g.tree-sitter-rust-format-args - g.tree-sitter-scss - g.tree-sitter-sql - g.tree-sitter-toml - g.tree-sitter-tsx - g.tree-sitter-typescript - g.tree-sitter-xml - g.tree-sitter-yaml - ])) - yaml-pro - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - envrc - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - consult-gh - diff-hl - forge - git-timemachine - magit - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - cape - consult - corfu - embark-consult - marginalia - orderless - vertico - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - org-modern - org-present - org-roam - verb - visual-fill-column - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - eat - ]; - }; - } - (mkIf (cfg.redbook.enable && cfg.desktop.enable) { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ + }) + teams + ]; + }) + (mkIf (cfg.redbook.enable && cfg.desktop.enable) { + programs.emacs = { + extraPackages = + epkgs: with epkgs; [ slack ox-slack ]; - extraConfig = builtins.readFile ./slack.el; - }; - }) - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - ledger-mode - ]; - }; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - age - ]; - }; - } - { - programs.claude-code = { - enable = true; - }; - - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - (epkgs.trivialBuild { - pname = "claude-code"; - version = "0.4.5"; - src = pkgs.fetchFromGitHub { - owner = "stevemolitor"; - repo = "claude-code.el"; - rev = "03199df8b3a1e9cd4857f0851f7a912ba524aff3"; - sha256 = "sha256-5QJrWIu4EgnHcOqMwlrs2JBBx7aI9OaSJswesr6Apfk="; - }; - packageRequires = [ epkgs.inheritenv ]; - }) - ]; - }; - } - { - programs.bat = { - config.theme = "catppuccin-frappe"; - themes = { - catppuccin-frappe = { - src = pkgs.fetchFromGitHub { - owner = "catppuccin"; - repo = "sublime-text"; # Bat uses sublime syntax for its themes - rev = "3d8625d937d89869476e94bc100192aa220ce44a"; - sha256 = "3ABUsfJpb6RO6AiuuSL5gwDofJIwC5tlEMzBrlY9/s0="; - }; - file = "Frappe.tmTheme"; - }; - }; - }; - } - { - programs.helix.settings.theme = "catppuccin_frappe"; - } - { - programs.tmux.plugins = [ - { - plugin = pkgs.tmuxPlugins.catppuccin; - extraConfig = ''set -g @catppuccin_window_tabs_enabled on - set -g @catppuccin_window_default_text "#W" - set -g @catppuccin_window_current_text "#W"''; - } - ]; - } - { - xdg = { - enable = false - ; - # configFile."lazygit/config.yml" = { - # enable = true; - # source = pkgs.fetchFromGitHub - # { - # owner = "catppuccin"; - # repo = "lazygit"; - # rev = "b2ecb6d41b6f54a82104879573c538e8bdaeb0bf"; # Replace with the latest commit hash - # sha256 = "9BBmWRcjNaJE9T0RKVEJaSnkrbMom0CLYE8PzAT6yFw="; - # } + /themes/frappe.yml; - # }; - }; - } - { - programs.starship.settings = builtins.fromTOML (builtins.readFile - (pkgs.fetchFromGitHub - { - owner = "catppuccin"; - repo = "starship"; - rev = "3e3e54410c3189053f4da7a7043261361a1ed1bc"; # Replace with the latest commit hash - sha256 = "soEBVlq3ULeiZFAdQYMRFuswIIhI9bclIU8WXjxd7oY="; - } + /palettes/frappe.toml)); - } - { - programs.ghostty.settings.theme = "Catppuccin Frappe"; - } - { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - catppuccin-theme - ]; - extraConfig = ''(defvar my-base-font-size ${if pkgs.stdenv.isDarwin then "130" else "100"}) - (require 'catppuccin-theme) - (setopt catppuccin-flavor 'frappe) - (load-theme 'catppuccin :no-confirm) - - ;; (set-face-attribute 'default nil :font "JetBrainsMono Nerd Font" :height 130) - ;; (set-face-attribute 'fixed-pitch nil :font "JetBrainsMono Nerd Font" :height 130) - (set-face-attribute 'default nil :font "Iosevka Term SS14" :height my-base-font-size) - (set-face-attribute 'fixed-pitch nil :font "Iosevka Term SS14" :height my-base-font-size) - (set-face-attribute 'variable-pitch nil :font "Iosevka Etoile" :height my-base-font-size) - ''; - }; - } - ]); - } + extraConfig = builtins.readFile ./slack.el; + }; + }) + ]); +} diff --git a/modules/home/emacs.el b/modules/home/emacs.el index 66fd63b..e326607 100644 --- a/modules/home/emacs.el +++ b/modules/home/emacs.el @@ -1,3 +1,15 @@ +(setq use-package-always-defer t) + +(use-package emacs + :defer nil + :config + (setq-default fill-column 120) + (setq-default tab-width 4)) + +(use-package simple + :config + (setq-default indent-tabs-mode nil)) + (setopt save-interprogram-paste-before-kill t) (setopt enable-recursive-minibuffers t) (setopt line-spacing 0.15) @@ -27,8 +39,6 @@ (column-number-mode t) -(which-key-mode) - (setopt dired-auto-revert-buffer t) (global-hl-line-mode) @@ -120,6 +130,34 @@ (setopt compilation-scroll-output 'first-error) +(use-package minibuffer + :bind (:map minibuffer-local-map + ("M-s" . consult-history) + ("M-r" . consult-history) ) + :custom + (completion-styles '(orderless basic))) + +(use-package xref + :custom + (xref-show-xrefs-function 'consult-xref) + (xref-show-definitions-function 'consult-xref)) + +(use-package register + :custom + (register-preview-delay 0.5) + :config + (advice-add 'register-preview :override #'consult-register-window)) + +(use-package isearch + :bind + (:map isearch-mode-map + ("M-e" . consult-isearch-history) + ("M-s e" . consult-isearch-history) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi)) + :custom + (isearch-wrap-pause 'no-ding)) + (defun jf/window-children (window) (let ((result nil) (child (window-child window))) @@ -352,21 +390,23 @@ falls back to the outer one first." ;; (evil-org-set-key-theme '(navigation insert textobjects additional calendar)) ;; (require 'evil-org-agenda) ;; (evil-org-agenda-set-keys) -(require 'god-mode) -(require 'which-key) +(use-package god-mode + :bind (("" . (lambda () (interactive) (god-mode-all 1))) + :map god-local-mode-map + ("i" . (lambda () (interactive) (god-mode-all -1))) + ("z" . repeat))) -(bind-keys :map god-local-mode-map - ("i" . (lambda () (interactive) (god-mode-all -1))) - ("z" . repeat)) -(bind-keys ("" . (lambda () (interactive) (god-mode-all 1)))) (defun my-god-mode-update-cursor-type () (setq cursor-type (cond ((and (featurep 'org-present) org-present-mode) nil) ((or (and (featurep 'god-mode) god-local-mode) buffer-read-only) 'box) (t 'bar)))) - (add-hook 'post-command-hook 'my-god-mode-update-cursor-type) -(which-key-enable-god-mode-support) + +(use-package which-key + :hook after-init + :config + (which-key-enable-god-mode-support)) (defun avy-goto-char-timer-embark () (interactive) @@ -382,19 +422,13 @@ falls back to the outer one first." (avy-goto-char-timer))) (bind-keys - ("C-j" . avy-goto-char-timer) - ("M-j" . avy-goto-char-timer-embark) - ("C-." . embark-act)) + ("M-j" . avy-goto-char-timer-embark)) -(with-eval-after-load 'lispy - (bind-keys :map lispy-mode-map - ("C-j" . avy-goto-char-timer))) +(use-package avy + :bind (("C-j" . avy-goto-char-timer))) -(with-eval-after-load 'org - (bind-keys :map org-mode-map - ("C-j" . avy-goto-char-timer))) - -(setopt isearch-wrap-pause 'no-ding) +(use-package embark + :bind (("C-." . embark-act))) (defun string-inflection-rust-style-cycle-function (str) (cond @@ -416,36 +450,44 @@ falls back to the outer one first." (bind-keys :repeat-map string-inflection-rust-map ("u" . string-inflection-rust-style-cycle)) -(bind-keys ("C-x 4 n" . edit-indirect-region)) +(use-package edit-indirect + :bind (("C-x 4 n" . edit-indirect-region))) -(with-eval-after-load 'apheleia +(use-package apheleia + :config (push '(prisma-format "prisma" "format" "--schema" filepath) apheleia-formatters)) -(buffer-terminator-mode 1) +(use-package buffer-terminator + :hook after-init) -(bind-keys - ("s-b" . consult-project-buffer)) +(use-package elec-pair + :hook (after-init . electric-pair-mode)) -(electric-pair-mode 1) +(use-package tree-sitter + :hook (after-init . global-tree-sitter-mode)) -(setq-default indent-tabs-mode nil) -(setq-default tab-width 4) -(global-tree-sitter-mode 1) - -(setopt ediff-split-window-function 'split-window-horizontally) +(use-package ediff-wind + :custom + (ediff-split-window-function 'split-window-horizontally)) (defun project-nix-flake () (interactive) (nix-flake (project-root (project-current t)))) (bind-keys ("C-x p n" . project-nix-flake)) -(setq-default fill-column 120) +(use-package ansi-color + :hook + (compilation-filter . ansi-color-compilation-filter)) -(defun jf-init-fill-column () - (display-fill-column-indicator-mode 1)) -(add-hook 'prog-mode-hook 'jf-init-fill-column) +(use-package display-fill-column-indicator + :hook (prog-mode)) -(with-eval-after-load 'prog-mode +(use-package prog-mode + :bind (:map prog-mode-map + ("C-c D" . flymake-show-project-diagnostics) + ("M-n" . flymake-goto-next-error) + ("M-p" . flymake-goto-prev-error)) + :config (defun my-prog-fill-reindent-defun (&optional argument) "Refill or reindent the paragraph or defun that contains point. @@ -472,72 +514,89 @@ or follows point." (advice-add 'prog-fill-reindent-defun :override #'my-prog-fill-reindent-defun)) -(add-hook 'compilation-filter-hook 'ansi-color-compilation-filter) - -(autoload 'flymake-show-project-diagnostics "flymake") -(autoload 'flymake-goto-next-error "flymake") -(autoload 'flymake-goto-prev-error "flymake") - -(with-eval-after-load 'prog-mode - (bind-keys :map prog-mode-map - ("C-c D" . flymake-show-project-diagnostics) - ("M-n" . flymake-goto-next-error) - ("M-p" . flymake-goto-prev-error))) - -(with-eval-after-load 'flymake - (bind-keys :map flymake-project-diagnostics-mode-map - ("q" . quit-window))) - -(setopt eglot-ignored-server-capabilities '(:inlayHintProvider :semanticTokensProvider)) -(add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)) nil t) -(setopt eglot-code-action-indications nil) -(setopt eglot-events-buffer-config '(:size 300000 :format full)) - -(with-eval-after-load 'eglot - (bind-keys :map eglot-mode-map - ("C-c a" . eglot-code-actions) - ("C-c r" . eglot-rename) - ("C-c i" . eglot-find-implementation))) - -(with-eval-after-load 'eglot - (add-to-list 'eglot-stay-out-of 'yasnippet)) +(use-package flymake + :commands (flymake-show-project-diagnostics flymake-goto-next-error flymake-goto-prev-error) + :bind (:map flymake-project-diagnostics-mode-map + ("q" . quit-window))) (setq major-mode-remap-alist '((rust-mode . rust-mode))) - -(setopt rust-mode-treesitter-derive t) - -(with-eval-after-load 'rust-prog-mode - (bind-keys :map rust-mode-map - ("C-c u" . string-inflection-rust-style-cycle))) +(use-package rust-mode + :bind (:map rust-mode-map + ("C-c u" . string-inflection-rust-style-cycle)) + :custom + (rust-mode-treesitter-derive t)) (defun yaml-validate () (interactive) (compile (concat "yamllint -d '{extends: default, rules: {indentation: {indent-sequences: false}, braces: {max-spaces-inside: 1}}}' " (buffer-file-name)) t)) -(add-hook 'yaml-ts-mode-hook 'yaml-pro-ts-mode) +(use-package yaml-pro + :hook (yaml-ts-mode . yaml-pro-ts-mode)) -(setopt inferior-lisp-program "sbcl") - -(setopt lispy-compat '(god-mode edebug)) -(add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1))) -(add-hook 'lisp-mode-hook (lambda () (lispy-mode 1))) -(add-hook 'lisp-data-mode-hook (lambda () (lispy-mode 1))) -(with-eval-after-load 'lispy - ;; (require 'lispyville) - ;; (add-hook 'lispy-mode-hook #'lispyville-mode) - (bind-keys :map lispy-mode-map - ("i" . (lambda () - (interactive) - (if (and (featurep 'god-mode) god-global-mode) - (god-mode-all -1) - (special-lispy-tab)))))) +(use-package lispy + :hook (emacs-lisp-mode lisp-mode lisp-data-mode) + :bind (:map lispy-mode-map + ("i" . (lambda () + (interactive) + (if (and (featurep 'god-mode) god-global-mode) + (god-mode-all -1) + (special-lispy-tab)))) + ("C-j" . avy-goto-char-timer)) + :custom + (lispy-compat '(god-mode edebug))) (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode)) -(add-to-list 'major-mode-remap-alist '(typescript-mode . typescript-ts-mode)) (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-mode)) -(add-to-list 'major-mode-remap-alist '(tsx-mode . tsx-ts-mode)) -(setopt typescript-ts-mode-indent-offset 2) -(with-eval-after-load 'typescript-ts-mode + +(use-package typescript-ts-mode + :init + (add-to-list 'major-mode-remap-alist '(typescript-mode . typescript-ts-mode)) + (add-to-list 'major-mode-remap-alist '(tsx-mode . tsx-ts-mode)) + (defvar my/edit-indirect-tag-modes + '(("graphql" . graphql-ts-mode) + ("gql" . graphql-ts-mode) + ("sql" . sql-mode) + ("css" . css-mode)) + "Map of tagged-template tag names to major modes.") + + (defun my/edit-indirect--node-bounds (node) + "Return (BEG . END) for NODE's contents, excluding delimiters." + (cons (1+ (treesit-node-start node)) + (1- (treesit-node-end node)))) + + (defun my/edit-indirect-tagged-template-at-point () + (interactive) + (let* ((node (treesit-node-at (point))) + (tmpl-node (treesit-parent-until + node + (lambda (n) (equal (treesit-node-type n) "template_string")))) + (str-node (unless tmpl-node + (treesit-parent-until + node + (lambda (n) (equal (treesit-node-type n) "string"))))) + (target-node (or tmpl-node str-node))) + (unless target-node + (user-error "Point is not inside a string or template literal")) + (let* ((tagged-node (and tmpl-node (treesit-node-parent tmpl-node))) + (tag-node (and tagged-node + (equal (treesit-node-type tagged-node) "call_expression") + (treesit-node-child-by-field-name tagged-node "function"))) + (tag-name (and tag-node (treesit-node-text tag-node t))) + (mode (cond + (tag-name (or (cdr (assoc tag-name my/edit-indirect-tag-modes)) + major-mode)) + (t 'text-mode))) + (bounds (my/edit-indirect--node-bounds target-node)) + (buf (edit-indirect-region (car bounds) (cdr bounds) t))) + (with-current-buffer buf + (funcall mode))))) + :bind + (:map typescript-ts-mode-map + ("C-c '" . my/edit-indirect-tagged-template-at-point) + :map tsx-ts-mode-map + ("C-c '" . my/edit-indirect-tagged-template-at-point)) + :custom (typescript-ts-mode-indent-offset 2) + :config (defun my-tsx-ts-mode--indent-compatibility-b893426 () "Indent rules helper, to handle different releases of tree-sitter-tsx. Check if a node type is available, then return the right indent rules." @@ -551,56 +610,26 @@ Check if a node type is available, then return the right indent rules." ((parent-is "jsx_text") grand-parent typescript-ts-mode-indent-offset))))) (advice-add 'tsx-ts-mode--indent-compatibility-b893426 :override #'my-tsx-ts-mode--indent-compatibility-b893426)) -(defvar my/edit-indirect-tag-modes - '(("graphql" . graphql-ts-mode) - ("gql" . graphql-ts-mode) - ("sql" . sql-mode) - ("css" . css-mode)) - "Map of tagged-template tag names to major modes.") -(defun my/edit-indirect--node-bounds (node) - "Return (BEG . END) for NODE's contents, excluding delimiters." - (cons (1+ (treesit-node-start node)) - (1- (treesit-node-end node)))) +(use-package eglot + :bind (:map eglot-mode-map + ("C-c a" . eglot-code-actions) + ("C-c r" . eglot-rename) + ("C-c i" . eglot-find-implementation)) + :custom + (eglot-ignored-server-capabilities '(:inlayHintProvider :semanticTokensProvider)) + (eglot-code-action-indications nil) + (eglot-events-buffer-config '(:size 300000 :format full)) + :config + (add-to-list 'eglot-server-programs '((prisma-ts-mode) "prisma-language-server" "--stdio")) + (add-to-list 'eglot-stay-out-of 'yasnippet) + (add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)) nil t)) -(defun my/edit-indirect-tagged-template-at-point () - (interactive) - (let* ((node (treesit-node-at (point))) - (tmpl-node (treesit-parent-until - node - (lambda (n) (equal (treesit-node-type n) "template_string")))) - (str-node (unless tmpl-node - (treesit-parent-until - node - (lambda (n) (equal (treesit-node-type n) "string"))))) - (target-node (or tmpl-node str-node))) - (unless target-node - (user-error "Point is not inside a string or template literal")) - (let* ((tagged-node (and tmpl-node (treesit-node-parent tmpl-node))) - (tag-node (and tagged-node - (equal (treesit-node-type tagged-node) "call_expression") - (treesit-node-child-by-field-name tagged-node "function"))) - (tag-name (and tag-node (treesit-node-text tag-node t))) - (mode (cond - (tag-name (or (cdr (assoc tag-name my/edit-indirect-tag-modes)) - major-mode)) - (t 'text-mode))) - (bounds (my/edit-indirect--node-bounds target-node)) - (buf (edit-indirect-region (car bounds) (cdr bounds) t))) - (with-current-buffer buf - (funcall mode))))) - -(with-eval-after-load 'typescript-ts-mode - (define-key typescript-ts-mode-map (kbd "C-c '") #'my/edit-indirect-tagged-template-at-point) - (define-key tsx-ts-mode-map (kbd "C-c '") #'my/edit-indirect-tagged-template-at-point)) - -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs '((prisma-ts-mode) "prisma-language-server" "--stdio"))) - -(push '(java-mode . java-ts-mode) major-mode-remap-alist) -(with-eval-after-load 'java-ts-mode - (bind-keys :map java-ts-mode-map - ("C-c u" . string-inflection-java-style-cycle)) +(use-package java-ts-mode + :init + (push '(java-mode . java-ts-mode) major-mode-remap-alist) + :bind (:map java-ts-mode-map + ("C-c u" . string-inflection-java-style-cycle)) (push `((n-p-gp nil "block" "lambda_expression") parent-bol java-ts-mode-indent-offset) (cdar java-ts-mode--indent-rules)) (push `((n-p-gp "}" "block" "lambda_expression") parent-bol 0) @@ -629,86 +658,84 @@ Check if a node type is available, then return the right indent rules." ; jf/compile-class-to-file 3))) ;(add-to-list 'compilation-error-regexp-alist 'java-stack-trace) -(add-hook 'sql-mode-hook 'sqlind-minor-mode) +(use-package sql-indent + :hook + (sql-mode . sqlind-minor-mode)) -(setopt haskell-ts-use-indent t) +(use-package haskell-ts-mode + :custom + (haskell-ts-use-indent t)) -(add-hook 'after-init-hook 'envrc-global-mode) -(setopt envrc-show-summary-in-minibuffer nil) +(use-package envrc + :hook + (after-init . envrc-global-mode) + :custom + (envrc-show-summary-in-minibuffer nil)) -(setopt magit-define-global-key-bindings 'recommended) -(bind-keys ("s-g" . magit)) - -(setopt magit-save-repository-buffers 'dontask) -(setopt magit-commit-show-diff nil) -(with-eval-after-load 'magit +(use-package magit + :bind (("s-g" . magit)) + :custom + (magit-define-global-key-bindings 'recommended) + (magit-save-repository-buffers 'dontask) + (magit-commit-show-diff nil) + :config (set-face-attribute 'git-commit-summary nil :inherit nil) (defun magit-fetch-all-on-load () (magit-run-git-async "fetch" "--all")) (add-hook 'magit-status-mode-hook 'magit-fetch-all-on-load) - (remove-hook 'magit-status-headers-hook 'magit-insert-tags-header) - (require 'forge) - (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)) + (remove-hook 'magit-status-headers-hook 'magit-insert-tags-header)) -(with-eval-after-load 'forge +(use-package forge + :after magit + :config (add-hook 'forge-edit-post-hook 'forge-create-pullreq-insert-single-commit-message) (setopt forge-status-buffer-default-topic-filters (forge--topics-spec :type 'topic :active nil :state 'open :order 'newest))) -(global-diff-hl-mode) +(use-package diff-hl + :hook + ((after-init . global-diff-hl-mode) + (magit-post-refresh . diff-hl-magit-post-refresh))) -(bind-keys - ;; ("C-c M-x" . consult-mode-command) - ("C-c h" . consult-history) - ;; ("C-c k" . consult-kmacro) - ;; ("C-c m" . consult-man) - ;; ([remap Info-search] . consult-info) +(use-package consult + :commands (consult-history) + :functions (consult-register-window consult-xref) + :binds (;; ("C-c M-x" . consult-mode-command) + ("s-b" . consult-project-buffer) + ("C-c h" . consult-history) + ;; ("C-c k" . consult-kmacro) + ;; ("C-c m" . consult-man) + ;; ([remap Info-search] . consult-info) - ;; ("C-x M-:" . consult-complex-command) - ("C-x b" . consult-buffer) - ("s-B" . consult-buffer) - ("C-x 4 b" . consult-buffer-other-window) - ("C-x 5 b" . consult-buffer-other-frame) - ;; ("C-x t b" . consult-buffer-other-tab) - ("C-x r b" . consult-bookmark) - ("C-x p b" . consult-project-buffer) + ;; ("C-x M-:" . consult-complex-command) + ("C-x b" . consult-buffer) + ("s-B" . consult-buffer) + ("C-x 4 b" . consult-buffer-other-window) + ("C-x 5 b" . consult-buffer-other-frame) + ;; ("C-x t b" . consult-buffer-other-tab) + ("C-x r b" . consult-bookmark) + ("C-x p b" . consult-project-buffer) - ("M-#" . consult-register-load) - ("M-'" . consult-register-store) - ("C-M-#" . consult-register) - ("M-y" . consult-yank-pop) - ("M-g e" . consult-compile-error) - ("M-g f" . consult-flymake) - ("M-g g" . consult-goto-line) - ("M-g M-g" . consult-goto-line) - ("M-g o" . consult-org-heading) - ("M-g m" . consult-mark) - ("M-g k" . consult-global-mark) - ("M-g i" . consult-imenu) - ("M-g I" . consult-imenu-multi) - ("M-s r" . consult-ripgrep) - ("M-s l" . consult-line) - ("M-s L" . consult-line-multi) - ("M-s e" . consult-isearch-history) - :map isearch-mode-map - ("M-e" . consult-isearch-history) - ("M-s e" . consult-isearch-history) - ("M-s l" . consult-line) - ("M-s L" . consult-line-multi) - :map minibuffer-local-map - ("M-s" . consult-history) - ("M-r" . consult-history)) - -(advice-add 'register-preview :override #'consult-register-window) -(setopt register-preview-delay 0.5) - -(setopt xref-show-xrefs-function 'consult-xref) -(setopt xref-show-definitions-function 'consult-xref) - -(eval-when-compile - (require 'consult)) - -(with-eval-after-load 'consult + ("M-#" . consult-register-load) + ("M-'" . consult-register-store) + ("C-M-#" . consult-register) + ("M-y" . consult-yank-pop) + ("M-g e" . consult-compile-error) + ("M-g f" . consult-flymake) + ("M-g g" . consult-goto-line) + ("M-g M-g" . consult-goto-line) + ("M-g o" . consult-org-heading) + ("M-g m" . consult-mark) + ("M-g k" . consult-global-mark) + ("M-g i" . consult-imenu) + ("M-g I" . consult-imenu-multi) + ("M-s r" . consult-ripgrep) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ("M-s e" . consult-isearch-history)) + :custom + (consult-narrow-key "<") + :config (consult-customize consult-theme :preview-key '(:debounce 0.2 any) consult-ripgrep consult-git-grep consult-grep consult-man @@ -720,38 +747,37 @@ Check if a node type is available, then return the right indent rules." consult-source-project-recent-file :preview-key '(:debounce 0.4 any))) -(setopt consult-narrow-key "<") ;; "C-+" - (use-package vertico + :hook + (after-init . vertico-mode) :custom - (vertico-count 25) - :config - (vertico-mode)) - -(setopt completion-styles '(orderless basic)) + (vertico-count 25)) (use-package marginalia - :config - (marginalia-mode)) + :hook + (after-init . marginalia-mode)) (use-package corfu + :hook + (after-init . global-corfu-mode) :custom (corfu-cycle t) (corfu-popupinfo-mode nil) (corfu-quit-at-boundary nil) - (corfu-on-exact-match 'show) - :config - (global-corfu-mode)) + (corfu-on-exact-match 'show)) ;; (setq completion-in-region-function #'consult-completion-in-region) (use-package cape - :custom + :functions (cape-file) + :init (add-hook 'completion-at-point-functions 'cape-file)) (use-package org :bind (("C-c A" . org-agenda) ("C-c C" . org-capture) - ("C-c l" . org-id-store-link)) + ("C-c l" . org-id-store-link) + :map org-mode-map + ("C-j" . avy-goto-char-timer)) :custom (org-babel-load-languages '((emacs-lisp . t) (sql . t))) (org-confirm-babel-evaluate nil) @@ -815,7 +841,7 @@ Check if a node type is available, then return the right indent rules." (org-level-7 :height 3.0) (org-level-8 :height 3.0))) (push (apply #'face-remap-add-relative face) my/org-present-face-remapping-cookies)) - + ;; Set a blank header line string to create blank space at the top (setq header-line-format " ") @@ -833,7 +859,7 @@ Check if a node type is available, then return the right indent rules." ;; Reset font customizations (dolist (cookie my/org-present-face-remapping-cookies) (face-remap-remove-relative cookie)) - + ;; Clear the header line string so that it isn't displayed (setq header-line-format nil) @@ -869,12 +895,13 @@ Check if a node type is available, then return the right indent rules." :hook (eshell-load-hook . eat-eshell-mode)) (use-package age + :hook + (after-init . age-file-enable) :custom (age-default-identity "~/source/dotfiles/keys/yubi1age.txt") (age-default-recipient "~/source/dotfiles/keys/myself.txt") :config - (setenv "PINENTRY_PROGRAM" "pinentry-mac") - (age-file-enable)) + (setenv "PINENTRY_PROGRAM" "pinentry-mac")) (use-package claude-code :bind-keymap ("C-c c" . claude-code-command-map) diff --git a/modules/home/emacs.nix b/modules/home/emacs.nix new file mode 100644 index 0000000..ef341ac --- /dev/null +++ b/modules/home/emacs.nix @@ -0,0 +1,137 @@ +{ + config, + lib, + pkgs, + ... +}: +{ + config = lib.mkIf (config.jfrikker.enable && config.jfrikker.desktop.enable) { + programs.emacs = { + enable = true; + extraPackages = + epkgs: with epkgs; [ + ace-window + age + apheleia + avy + buffer-terminator + cape + catppuccin-theme + (epkgs.trivialBuild { + pname = "claude-code"; + version = "0.4.5"; + src = pkgs.fetchFromGitHub { + owner = "stevemolitor"; + repo = "claude-code.el"; + rev = "03199df8b3a1e9cd4857f0851f7a912ba524aff3"; + sha256 = "sha256-5QJrWIu4EgnHcOqMwlrs2JBBx7aI9OaSJswesr6Apfk="; + }; + packageRequires = [ epkgs.inheritenv ]; + }) + consult + consult-eglot + consult-gh + corfu + diff-hl + doom-modeline + eat + edit-indirect + embark + embark-consult + envrc + forge + free-keys + git-timemachine + god-mode + graphql-ts-mode + haskell-ts-mode + jenkinsfile-mode + jq-mode + ligature + (lispy.overrideAttrs { + packageRequires = [ + ace-window + iedit + swiper + zoutline + hydra + ]; + ignoreCompilationError = true; + }) + ledger-mode + lua-mode + magit + marginalia + mixed-pitch + nix-mode + orderless + org-modern + org-present + org-roam + pnpm-mode + popper + prisma-ts-mode + rust-mode + sly + sly-macrostep + sql-indent + string-inflection + tree-sitter + (treesit-grammars.with-grammars (g: [ + g.tree-sitter-bash + g.tree-sitter-commonlisp + g.tree-sitter-css + g.tree-sitter-dockerfile + g.tree-sitter-dot + g.tree-sitter-elisp + g.tree-sitter-graphql + g.tree-sitter-gitcommit + g.tree-sitter-gitignore + g.tree-sitter-haskell + g.tree-sitter-html + g.tree-sitter-java + g.tree-sitter-javascript + g.tree-sitter-jq + g.tree-sitter-json + g.tree-sitter-json5 + g.tree-sitter-just + g.tree-sitter-ledger + g.tree-sitter-llvm + g.tree-sitter-make + g.tree-sitter-nginx + g.tree-sitter-nix + g.tree-sitter-org + g.tree-sitter-php + g.tree-sitter-prisma + g.tree-sitter-python + g.tree-sitter-rust + g.tree-sitter-rust-format-args + g.tree-sitter-scss + g.tree-sitter-sql + g.tree-sitter-toml + g.tree-sitter-tsx + g.tree-sitter-typescript + g.tree-sitter-xml + g.tree-sitter-yaml + ])) + verb + vertico + visual-fill-column + yaml-pro + ]; + extraConfig = '' + (defvar my-base-font-size ${if pkgs.stdenv.isDarwin then "130" else "100"}) + (require 'catppuccin-theme) + (setopt catppuccin-flavor 'frappe) + (load-theme 'catppuccin :no-confirm) + + ;; (set-face-attribute 'default nil :font "JetBrainsMono Nerd Font" :height 130) + ;; (set-face-attribute 'fixed-pitch nil :font "JetBrainsMono Nerd Font" :height 130) + (set-face-attribute 'default nil :font "Iosevka Term SS14" :height my-base-font-size) + (set-face-attribute 'fixed-pitch nil :font "Iosevka Term SS14" :height my-base-font-size) + (set-face-attribute 'variable-pitch nil :font "Iosevka Etoile" :height my-base-font-size) + '' + + builtins.readFile ./emacs.el; + }; + }; +} diff --git a/modules/home/helix.nix b/modules/home/helix.nix new file mode 100644 index 0000000..d0d54f1 --- /dev/null +++ b/modules/home/helix.nix @@ -0,0 +1,79 @@ +{ config, lib, ... }: { + config = lib.mkIf config.jfrikker.enable { + programs.helix = { + enable = true; + defaultEditor = true; + + settings.editor = { + auto-format = false; + auto-pairs = false; + auto-save.after-delay.enable = true; + auto-save.after-delay.timeout = 1000; + cursorline = true; + idle-timeout = 0; + rulers = [ 120 ]; + text-width = 100; + lsp.goto-reference-include-declaration = false; + cursor-shape.insert = "bar"; + smart-tab.supersede-menu = true; + whitespace.render.newline = "all"; + }; + + settings.keys.normal.space = { + space = "file_picker"; + "," = "buffer_picker"; + # b = "@:sh wezterm cli spawn --cwd . -- tig blame %"; + }; + + settings.theme = "catppuccin_frappe"; + + languages.language-server.eslint = { + args = [ "--stdio" ]; + command = "vscode-eslint-language-server"; + config.nodePath = ""; + config.quiet = false; + config.rulesCustomizations = [ ]; + config.run = "onType"; + config.validate = "on"; + config.codeAction.disableRuleComment.enable = true; + config.codeAction.disableRuleComment.location = "separateLine"; + config.codeAction.showDocumentation.enable = true; + config.problems.shortenToSingleLine = false; + }; + + languages.language-server.jdtls.config.java = { + autobuild.enabled = true; + completion.maxResults = 1000; + format.settings.url = "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml"; + }; + + languages.language-server.rust-analyzer.config.files.watcher = "client"; + + languages.language = [ + { + name = "typescript"; + language-servers = [ + "typescript-language-server" + "vscode-eslint-language-server" + ]; + indent.tab-width = 4; + indent.unit = " "; + } + { + name = "tsx"; + language-servers = [ + "typescript-language-server" + "vscode-eslint-language-server" + ]; + indent.tab-width = 4; + indent.unit = " "; + } + { + name = "java"; + indent.tab-width = 4; + indent.unit = " "; + } + ]; + }; + }; +} diff --git a/modules/home/starship.nix b/modules/home/starship.nix new file mode 100644 index 0000000..818fd8a --- /dev/null +++ b/modules/home/starship.nix @@ -0,0 +1,36 @@ +{ pkgs, ... }: { + config.programs.starship = { + enable = true; + settings = { + add_newline = false; + container.disabled = true; + git_branch.symbol = " "; + git_status.ahead = "⇡\${count}"; + git_status.behind = "⇣\${count}"; + git_status.diverged = "⇕⇡\${ahead_count}⇣\${behind_count}"; + git_status.format = "([\\[\$conflicted\$staged\$ahead_behind\\]](\$style) )"; + java.symbol = " "; + line_break.disabled = true; + nix_shell.symbol = " "; + nodejs.symbol = " "; + package.disabled = true; + palette = "catppuccin_frappe"; + python.symbol = " "; + package.symbol = " "; + rust.symbol = " "; + docker_context.disabled = true; + shell.disabled = false; + } + // builtins.fromTOML ( + builtins.readFile ( + pkgs.fetchFromGitHub { + owner = "catppuccin"; + repo = "starship"; + rev = "3e3e54410c3189053f4da7a7043261361a1ed1bc"; # Replace with the latest commit hash + sha256 = "soEBVlq3ULeiZFAdQYMRFuswIIhI9bclIU8WXjxd7oY="; + } + + /palettes/frappe.toml + ) + ); + }; +} diff --git a/modules/home/tmux.nix b/modules/home/tmux.nix new file mode 100644 index 0000000..08af026 --- /dev/null +++ b/modules/home/tmux.nix @@ -0,0 +1,34 @@ +{ pkgs, ... }: { + config.programs.tmux = { + baseIndex = 1; + clock24 = true; + customPaneNavigationAndResize = true; + historyLimit = 50000; + enable = true; + escapeTime = 0; + keyMode = "vi"; + mouse = true; + shell = pkgs.zsh + "/bin/zsh"; + shortcut = "Space"; + tmuxinator.enable = true; + terminal = "xterm-256color"; + extraConfig = '' + bind-key -T copy-mode-vi 'v' send -X begin-selection + bind-key -T copy-mode-vi 'y' send -X copy-selection-and-cancel + + bind-key -T launcher g popup -h 100% -w 100% -E lazygit + bind-key g switch-client -Tlauncher + set -g default-command "" + ''; + plugins = [ + { + plugin = pkgs.tmuxPlugins.catppuccin; + extraConfig = '' + set -g @catppuccin_window_tabs_enabled on + set -g @catppuccin_window_default_text "#W" + set -g @catppuccin_window_current_text "#W"''; + + } + ]; + }; +}