More use-package

This commit is contained in:
Joe Frikker
2026-09-30 09:12:22 -04:00
parent d1808f48a2
commit 402002e6e6
2 changed files with 256 additions and 300 deletions
Generated
+12 -12
View File
@@ -8,11 +8,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1789074092,
"narHash": "sha256-QCXTUbd4FuwOHO7LgJws78YTRcco4xgR8RoYgKLvd9w=",
"lastModified": 1790280108,
"narHash": "sha256-bApGLOF5XDhf3UCM1/e1yIQCoY5wW1+B88lHkcYlp2U=",
"owner": "sadjow",
"repo": "claude-code-nix",
"rev": "1f88b393ac054378c393f914dc83d08a6ef5daeb",
"rev": "f10421316cc6df045497f5aa0009adce648e89ee",
"type": "github"
},
"original": {
@@ -28,11 +28,11 @@
]
},
"locked": {
"lastModified": 1789267039,
"narHash": "sha256-LWiBv9yAYFi2LPbUhDGHPGKYskJQjj2fw12OlyO1uQo=",
"lastModified": 1790215976,
"narHash": "sha256-VkR1rMGyjU9jtw8ISFx5uqR9e+LEaOlhHwrseFkiIVk=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "ec172013fa62135f58fb58dd17ae9651e8f39727",
"rev": "a6631107a83ceab5872f298a2ea710859c80c4cb",
"type": "github"
},
"original": {
@@ -49,11 +49,11 @@
]
},
"locked": {
"lastModified": 1788680125,
"narHash": "sha256-amGSoDobwmp4CFvCn841ws2iuitus+HUJdD/gKgsrJA=",
"lastModified": 1789892076,
"narHash": "sha256-Dkg4VKPmDPqTwaiw2WH5br73tXoL1qrOO0xJnR4TlcA=",
"owner": "nix-community",
"repo": "nix-index-database",
"rev": "116ad1c2adb642405ef8916f6a94c8626f971344",
"rev": "9ad722673ab3b3f91f02135e53775825b240b869",
"type": "github"
},
"original": {
@@ -64,11 +64,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1789009968,
"narHash": "sha256-GB16oaxpsrnGNnGIE+0uYBqMRzB9X6FYDUvjvnJAYew=",
"lastModified": 1790218706,
"narHash": "sha256-6e4Na3z008XpdVyOXgfasXIn1aN+z8AXFG+jXdQSyuI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d58a46e3bc02d91ebe04667f8397752a749c0024",
"rev": "c508844df6c28fa6dabc1b6af70f3ccbd65c5201",
"type": "github"
},
"original": {
+244 -288
View File
@@ -866,13 +866,17 @@ Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh f
:custom (claude-code-optimize-window-resize nil))
(use-package gptel
:commands "gptel"
:bind (("C-c g" . gptel-menu))
:custom
(require 'org)
(gptel-default-mode 'org-mode)
(gptel-model 'claude-sonnet-5))
(use-package gptel-agent
:after gptel
:config
(require 'gptel-agent-tools-introspection)
(gptel-agent-update)
(gptel-make-preset 'coding
:description "Preset for agentic coding"
@@ -880,13 +884,26 @@ Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh f
:model 'claude-sonnet-5
:use-tools t
:tools '("Agent" "Skill" "TodoWrite" "Grep" "Read" "Glob" "Write" "Insert"
"Edit" "Mkdir" "Diagnostics" "WebFetch" "WebSearch" "Eval" "Bash"))
"Edit" "Mkdir" "Diagnostics" "WebFetch" "WebSearch" "Bash"))
(gptel-make-preset 'research
:description "Preset for agentic coding"
:description "Preset for answering questions with web results"
:backend "Claude"
:model 'claude-sonnet-5
:use-tools t
:tools '("WebFetch" "WebSearch")))
:confirm-tool-calls nil
:tools '("WebFetch" "WebSearch"))
(gptel-make-preset 'emacs-research
:description "Preset for answering questions about the live emacs instance"
:backend "Claude"
:model 'claude-sonnet-5
:use-tools t
:confirm-tool-calls nil
:tools '("WebFetch" "WebSearch"
"variable_completions" "command_completions" "function_completions"
"variable_documentation" "function_documentation" "variable_value"
"variable_source" "function_source" "symbol_manual_section"
"library_source" "manual_node_contents" "manual_nodes" "manual_names"
"features" "load_paths" "symbol_exists")))
#+END_SRC
** Age
@@ -1004,7 +1021,6 @@ Turn off some default settings that I don't find useful.
(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
@@ -1203,20 +1219,26 @@ A basic ace-window setup. Allows switching between windows quickly (although avy
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)
)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage = {
project = {
enable = true;
bind = {
"s-f" = "project-find-file";
"s-F" = "project-switch-project";
};
};
(with-eval-after-load 'tab-bar
(bind-keys :map tab-bar-mode-map
("s-s" . tab-bar-switch-to-tab)))
tab-bar = {
enable = true;
hook = ["after-init"];
bindLocal.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
@@ -1258,22 +1280,17 @@ Make compilation mode buffers auto-scroll to the first error:
(setopt compilation-scroll-output 'first-error)
#+END_SRC
#+begin_src elisp
(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)))
#+end_src
#+begin_src elisp
(use-package register
:defer t
:custom
(register-preview-delay 0.5)
:config
(advice-add 'register-preview :override #'consult-register-window))
#+begin_src nix
{
programs.emacs.init.usePackage.register = {
enable = true;
defer = true;
custom.register-preview-delay = 0.5;
config = ''
(advice-add 'register-preview :override #'consult-register-window)
'';
};
}
#+end_src
*** Window Placement
@@ -1282,7 +1299,79 @@ going for an IDE-style layout, with one main editor window, and auxiliary bottom
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
#+begin_src nix
{
programs.emacs.init.usePackage = {
window = {
enable = true;
custom = {
split-width-threshold = 200;
split-height-threshold = false;
display-buffer-base-action = '''((display-buffer-reuse-window
display-buffer-reuse-mode-window
display-buffer-use-some-window))'';
window-persistent-parameters = '''(cl-list* '(no-delete-other-windows . writable)
'(no-other-window . writable)
window-persistent-parameters)'';
};
init = ''
<<emacs-window-init>>
'';
config = ''
<<emacs-window-config>>
'';
};
popper = {
enable = true;
hook = ["after-init" "(after-init . popper-echo-mode)"];
bind = {
"C-q" = "popper-toggle";
"C-;" = "popper-cycle";
};
custom = {
popper-display-control = false;
popper-reference-buffers = ''(concat my/bottom-popups my/right-popups)'';
};
};
};
}
#+end_src
#+begin_src elisp :tangle no :noweb-ref emacs-window-init
(defvar jf/bottom-popups
'("\\*Flymake diagnostics .*"
"\\*Embark Export: .*"
"\\*Embark Collect .*"
"\\*xref\\*"
"\\*compilation\\*"
"\\*pnpm:.*"
"\\*sly-mrepl *"
"\\*lispy-message\\*"
"\\*occur\\*"
".*eshell\\*"
"\\*SQL: .*"))
(defvar jf/right-popups
'("magit: *"
"magit-log*"
"\\*Messages\\*"
"\\*trace-output\\*"
"\\*EGLOT connections\\*"
"magit-revision: .*"
"\\*Help\\*"
"\\*rustfmt\\*"
"\\*slack: .*"
"\\*forge: .*"
"new-pullreq"
"\\*claude:.*"
"\\*Man .*"
"\\*List .*"
"\\*Embark Actions\\*"
"\\*eldoc\\*"))
#+end_src
#+BEGIN_SRC elisp :tangle no :noweb-ref emacs-window-init
(defun jf/window-children (window)
(let ((result nil)
(child (window-child window)))
@@ -1343,188 +1432,48 @@ content to be.
(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 elisp :tangle no :noweb-ref emacs-window-config
(setq display-buffer-alist
`(((or ,@jf/bottom-popups)
(display-buffer-in-side-window)
(side . bottom)
(slot . 0)
(window-height . 0.33))
((or ,@jf/right-popups)
(display-buffer-reuse-window
display-buffer-reuse-mode-window
jf/display-buffer-right))))
#+end_src
#+BEGIN_SRC nix
*** Tramp
#+begin_src nix
{
programs.emacs = {
extraPackages = epkgs: with epkgs; [
apheleia
avy
edit-indirect
embark
god-mode
# evil
# evil-collection
# evil-org
string-inflection
];
programs.emacs.init.usePackage.tramp = {
enable = true;
package = (epkgs: epkgs.tramp.overrideAttrs {
version = "2.8.2.3";
src = pkgs.fetchurl {
url = "https://elpa.gnu.org/packages/tramp-2.8.2.3.tar";
sha256 = "sha256-8Rsn29+i7LDGDKFR1Q7HTml86MRmibN409D9QaGBci0=";
};
});
config = ''
<<emacs-tramp-config>>
'';
};
}
#+END_SRC
#+end_src
#+begin_src elisp :noweb no :noweb-ref emacs-tramp-config
(with-eval-after-load 'compile
(remove-hook 'compilation-mode-hook
#'tramp-compile-disable-ssh-controlmaster-options))
#+end_src
** Editing
This configuration affects general text editing, across all modes.
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
@@ -1537,98 +1486,99 @@ but I'm worried that having too many repeat maps will also be annoying (I'll hav
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 ("<escape>" . (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)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.god-mode = {
enable = true;
bind."<escape>" = "(lambda () (interactive) (god-mode-all 1))";
bindLocal.god-local-mode-map = {
i = "(lambda () (interactive) (god-mode-all -1))";
z = "repeat";
};
init = ''
(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)
'';
config = ''
(with-eval-after-load 'which-key
(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)
(goto-char pt)
(embark-act))))
(avy-goto-char-timer)))
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage = {
avy = {
enable = true;
functions = ["avy-action-oneshot"];
init = ''
(defun avy-goto-char-timer-embark ()
(interactive)
(let ((avy-action-oneshot
(lambda (pt)
(goto-char pt)
(embark-act))))
(avy-goto-char-timer)))
'';
bind = {
"C-j" = "avy-goto-char-timer";
"M-j" = "avy-goto-char-timer-embark";
};
};
(bind-keys
("C-j" . avy-goto-char-timer)
("M-j" . avy-goto-char-timer-embark)
("C-." . embark-act))
embark = {
enable = true;
bind."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)))
lispy.bindLocal.lispy-mode-map."C-j" = "avy-goto-char-timer";
org.bindLocal.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)
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.isearch = {
enable = true;
custom.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))
#+BEGIN_SRC nix
{
programs.emacs.init.usePackage.edit-indirect = {
enable = true;
bind."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))
#+begin_src nix
{
programs.emacs.init.usePackage.apheleia = {
enable = true;
config = ''
(push '(prisma-format "prisma" "format" "--schema" filepath) apheleia-formatters)
'';
};
}
#+end_src
** Workflow
@@ -1777,7 +1727,7 @@ available? It seems to work for now anyway...
programs.emacs.init.usePackage.ansi-color = {
enable = true;
defer = true;
init = "(add-hook 'compilation-filter 'ansi-color-compilation-filter)";
hook = ["(compilation-filter . ansi-color-compilation-filter)"];
};
}
#+end_src
@@ -1818,7 +1768,7 @@ I don't like UI elements that insert fake text into the file. They mess up the s
programs.emacs.init.usePackage.eglot = {
custom = {
eglot-ignored-server-capabilities = "'(:inlayHintProvider :semanticTokensProvider)";
eglot-code-actoins-indications = false;
eglot-code-actions-indications = false;
eglot-events-buffer-config = "'(:size 300000 :format full)";
};
config = "(add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)) nil t)";
@@ -1853,7 +1803,6 @@ for completion. That makes it really annoying to use; don't use yasnippet with e
programs.emacs.init.usePackage.rust-mode = {
enable = true;
mode = ["\"\\\\.rs\\\\'\""];
bindLocal.rust-mode-map."C-c u" = "string-inflection-rust-style-cycle";
custom.rust-mode-treesitter-derive = true;
};
}
@@ -2028,9 +1977,6 @@ for completion. That makes it really annoying to use; don't use yasnippet with e
java-ts-mode = {
enable = true;
init = ''(push '(java-mode . java-ts-mode) major-mode-remap-alist)'';
bindLocal.java-ts-mode-map = {
"C-c u" = "string-inflection-java-style-cycle";
};
config = ''
<<emacs-java-ts-mode-config>>
'';
@@ -2240,6 +2186,16 @@ xs
consult-eglot.enable = true;
isearch = {
enable = true;
bindLocal.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";
};
};
xref = {
enable = true;
defer = true;