From 53e0917a017e6807db1b960c909b80bf28e4a9f2 Mon Sep 17 00:00:00 2001 From: Joe Frikker Date: Thu, 24 Sep 2026 22:43:58 -0400 Subject: [PATCH] More usePackage I guess --- home.org | 1196 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 707 insertions(+), 489 deletions(-) diff --git a/home.org b/home.org index 2318398..667d910 100644 --- a/home.org +++ b/home.org @@ -155,6 +155,7 @@ are still Redbook-specific; ideally I'd find another way to install them. (mkIf cfg.redbook.enable { home.packages = with pkgs; [ openssh + postgresql_15 ]; }) #+END_SRC @@ -812,19 +813,49 @@ Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh f }; 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 ]; - }) - ]; + extraPackages = epkgs: with epkgs; + let gptel = (epkgs.melpaBuild { + pname = "gptel"; + version = "0.9.9.6"; + src = pkgs.fetchFromGitHub { + owner = "karthink"; + repo = "gptel"; + rev = "v0.9.9.6"; + sha256 = "sha256-jurm3AQz+NiKZh7ssJkQYP73EmBEoyjzuC4phARYBak="; + }; + recipe = pkgs.writeText "gptel-recipe" '' + (gptel :repo "karthink/gptel" :fetcher github) + ''; + packageRequires = [ epkgs.transient ]; + }); + in [ + (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 ]; + }) + (epkgs.melpaBuild { + pname = "gptel-agent"; + version = "20260823"; + src = pkgs.fetchFromGitHub { + owner = "karthink"; + repo = "gptel-agent"; + rev = "326e0abb8c6db0e5c4c421bd9af495932ae94b62"; + sha256 = "sha256-2eqY1MebzsDOpf3YEHy6o31TbpLA2iIPZMMVrscLYMk="; + }; + recipe = pkgs.writeText "gptel-agent-recipe" '' + (gptel-agent :fetcher github :repo "karthink/gptel-agent" :files (:defaults "agents")) + ''; + packageRequires = [ gptel ]; + }) + gptel + ]; }; } #+END_SRC @@ -833,6 +864,29 @@ Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh f (use-package claude-code :bind-keymap ("C-c c" . claude-code-command-map) :custom (claude-code-optimize-window-resize nil)) + + (use-package gptel + :custom + (require 'org) + (gptel-default-mode 'org-mode) + (gptel-model 'claude-sonnet-5)) + + (use-package gptel-agent + :config + (gptel-agent-update) + (gptel-make-preset 'coding + :description "Preset for agentic coding" + :backend "Claude" + :model 'claude-sonnet-5 + :use-tools t + :tools '("Agent" "Skill" "TodoWrite" "Grep" "Read" "Glob" "Write" "Insert" + "Edit" "Mkdir" "Diagnostics" "WebFetch" "WebSearch" "Eval" "Bash")) + (gptel-make-preset 'research + :description "Preset for agentic coding" + :backend "Claude" + :model 'claude-sonnet-5 + :use-tools t + :tools '("WebFetch" "WebSearch"))) #+END_SRC ** Age @@ -1030,7 +1084,7 @@ Similarly, highlighting the line the cursor is on is really useful for figuring especially on monitors with a lot of vertical space. #+BEGIN_SRC elisp - (global-hl-line-mode) + (add-hook 'after-init-hook 'global-hl-line-mode) #+END_SRC From the docs: "If ‘complete’, TAB first tries to indent the current line, and if the line was @@ -1053,14 +1107,14 @@ for two-character commands that need to be repeated; ~devil~ also has some singl repeats set up that only apply in that mode. #+BEGIN_SRC elisp - (repeat-mode) + (add-hook 'after-init-hook '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) + (add-hook 'after-init-hook '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. @@ -1095,7 +1149,9 @@ things look at little nicer. (setopt doom-modeline-irc nil) (setopt doom-modeline-workspace-name nil) (setopt doom-modeline-buffer-encoding nil) - (setopt doom-modeline-vcs-max-length 30) + (setopt doom-modeline-buffer-file-name-style 'relative-to-project) + ; (setopt doom-modeline-vcs-max-length 30) + (doom-modeline-remove-segment 'vcs 'main) (doom-modeline-mode) #+END_SRC @@ -1448,7 +1504,6 @@ content to be. window-persistent-parameters)) #+END_SRC - ** Editing This configuration affects general text editing, across all modes. First, some packages: @@ -1520,12 +1575,8 @@ his article. (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))))))) + (goto-char pt) + (embark-act)))) (avy-goto-char-timer))) (bind-keys @@ -1582,45 +1633,19 @@ Some auto-formatting stuff ** Workflow #+BEGIN_SRC nix { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - buffer-terminator - ]; + programs.emacs.init.usePackage.buffer-terminator = { + enable = true; + hook = ["after-init"]; }; } #+END_SRC -#+BEGIN_SRC elisp - (use-package buffer-terminator - :hook after-init) -#+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 @@ -1658,188 +1683,306 @@ Some auto-formatting stuff g.tree-sitter-xml g.tree-sitter-yaml ])) - yaml-pro ]; }; } #+END_SRC -#+BEGIN_SRC elisp - (use-package electric-pair - :hook after-init) +#+BEGIN_SRC nix + { + programs.emacs.init.usePackage = { + electric-pair = { + enable = true; + hook = [ "after-init" ]; + }; - (setq-default indent-tabs-mode nil) - (setq-default tab-width 4) + simple = { + enable = true; + config = '' + (setq-default indent-tabs-mode nil) + (setq-default tab-width 4) + ''; + }; - (use-package tree-sitter - :hook (after-init . global-tree-sitter-mode)) - - (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)) + ediff-wind = { + enable = true; + custom = { + ediff-split-window-function = "'split-window-horizontally"; + }; + }; + }; + } #+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) - - (use-package display-fill-column-indicator - :hook prog-mode) +#+begin_src nix + { + programs.emacs.init.usePackage.emacs = { + enable = true; + config = ''(setq-default fill-column 120)''; + }; + programs.emacs.init.usePackage.display-fill-column-indicator = { + enable = true; + hook = ["prog-mode"]; + }; + } #+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 - (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. +#+begin_src nix + { + programs.emacs.init.usePackage.prog-mode = { + enable = true; + defer = true; + config = '' + <> + ''; + }; + } +#+end_src + +#+begin_src elisp :tangle no :noweb-ref emacs-prog-mode-config + (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)))))) + (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)) + (advice-add 'prog-fill-reindent-defun :override #'my-prog-fill-reindent-defun) #+end_src -#+BEGIN_SRC elisp - (use-package ansi-color - :hook (compilation-filter . ansi-color-compilation-filter)) -#+END_SRC - -#+begin_src elisp - (use-package xref - :custom - (xref-show-xrefs-function 'consult-xref) - (xref-show-definitions-function 'consult-xref)) +#+begin_src nix + { + programs.emacs.init.usePackage.ansi-color = { + enable = true; + defer = true; + init = "(add-hook 'compilation-filter 'ansi-color-compilation-filter)"; + }; + } #+end_src *** Flymake -#+begin_src elisp - (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))) +#+begin_src nix + { + programs.emacs.init.usePackage.flymake = { + enable = true; + command = ["flymake-show-project-diagnostics" "flymake-goto-next-error" "flymake-goto-prev-error"]; + bindLocal.flymake-project-diagnositics-mode-map = { + "q" = "quit-window"; + }; + }; + + programs.emacs.init.usePackage.prog-mode = { + enable = true; + bindLocal.prog-mode-map = { + "C-c D" = "flymake-show-project-diagnostics"; + "M-n" = "flymake-goto-next-error"; + "M-p" = "flymake-goto-prev-error"; + }; + }; + } #+end_src *** Eglot +#+begin_src nix + { + programs.emacs.init.usePackage.eglot.enable = true; + } +#+end_src + 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 +#+begin_src nix + { + programs.emacs.init.usePackage.eglot = { + custom = { + eglot-ignored-server-capabilities = "'(:inlayHintProvider :semanticTokensProvider)"; + eglot-code-actoins-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)"; + }; + } +#+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))) +#+begin_src nix + { + programs.emacs.init.usePackage.eglot.bindLocal.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)) +#+begin_src nix + { + programs.emacs.init.usePackage.eglot.config = "(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 +#+begin_src nix + { + 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; + }; + } +#+end_src *** YAML -#+BEGIN_SRC elisp +#+begin_src nix + { + programs.emacs.init.usePackage.yaml-pro = { + enable = true; + init = '' + <> + ''; + hook = ["yaml-ts-mode"]; + }; + } +#+end_src + +#+BEGIN_SRC elisp :tangle no :noweb-ref emacs-yaml-pro-init (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") +#+begin_src nix + { + programs.emacs.init.usePackage.inf-lisp = { + enable = true; + custom.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 + programs.emacs.init.usePackage.sly = { + enable = true; + command = ["sly"]; + }; + + programs.emacs.init.usePackage.sly-macrostep = { + enable = true; + after = ["sly"]; + }; + + programs.emacs.init.usePackage.lispy = { + enable = true; + package = (epkgs: with epkgs; + lispy.overrideAttrs { + packageRequires = [ + ace-window iedit swiper zoutline hydra + ]; + ignoreCompilationError = true; + }); + hook = ["emacs-lisp-mode-hook" "lisp-mode-hook" "lisp-data-mode-hook"]; + bindLocal.lispy-mode-map = { + i = "(lambda () + (interactive) + (if (and (featurep 'god-mode) god-global-mode) + (god-mode-all -1) + (special-lispy-tab)))"; + }; + custom.lispy-compat = "'(god-mode edebug)"; + }; + } +#+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. +#+BEGIN_SRC nix + { + programs.emacs.init.usePackage = { + typescript-ts-mode = { + enable = true; + mode = ["\"\\\\.ts\\\\'\"" "(\"\\\\.tsx\\\\'\" . tsx-ts-mode)"]; + bindLocal = { + typescript-ts-mode-map = { + "C-c '" = "my/edit-indirect-tagged-template-at-point"; + }; + tsx-ts-mode-map = { + "C-c '" = "my/edit-indirect-tagged-template-at-point"; + }; + }; + custom.typescript-ts-mode-indent-offset = 2; + config = '' + <> + ''; + }; + + prisma-ts-mode = { + enable = true; + mode = ["\"\\\\.prisma\\\\'\""]; + }; + + eglot = { + config = ''(add-to-list 'eglot-server-programs '((prisma-ts-mode) "prisma-language-server" "--stdio"))''; + }; + + pnpm-mode = { + enable = true; + command = ["pnpm-mode-pnpm-run"]; + }; + + graphql-ts-mode = { + enable = true; + command = ["graphql-ts-mode"]; + }; + + jq-mode = { + enable = true; + mode = ["\"\\\\.jq\\\\'\""]; + interpreter = ["jq"]; + }; + }; + } +#+END_SRC + +#+BEGIN_SRC elisp :tangle no :noweb-ref emacs-typescript-ts-mode-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." - ;; 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)) + ;; 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) @@ -1879,275 +2022,358 @@ for completion. That makes it really annoying to use; don't use yasnippet with e (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) +#+BEGIN_SRC nix + { + programs.emacs.init.usePackage = { + 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 = '' + <> + ''; + }; + }; + } #+END_SRC +#+BEGIN_SRC elisp :tangle no :noweb-ref emacs-java-ts-mode-config + (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 + +*** Nix +#+begin_src nix + { + programs.emacs.init.usePackage = { + nix-mode = { + enable = true; + mode = ["\"\\\\.nix\\\\'\""]; + }; + + nix-flake = { + enable = true; + init = '' + (defun project-nix-flake () + (interactive) + (nix-flake (project-root (project-current t)))) + (bind-keys ("C-x p n" . project-nix-flake)) + ''; + command = [ "nix-flake" ]; + }; + }; + } +#+end_src +xs *** SQL -#+BEGIN_SRC elisp - (use-package sql-indent - :hook (sql-mode . sqlind-minor-mode)) +#+BEGIN_SRC nix + { + programs.emacs.init.usePackage.sql-indent = { + enable = true; + hook = ["(sql-mode . sqlind-minor-mode)"]; + }; + } #+END_SRC *** Haskell -#+BEGIN_SRC elisp - (use-package haskell-ts-mode - :mode "\\.hs\\'" - :custom - (haskell-ts-use-indent t)) +#+BEGIN_SRC nix + { + programs.emacs.init.usePackage.haskell-ts-mode = { + enable = true; + mode = ["\"\\\\.hs\\\\'\""]; + custom.haskell-ts-use-indent = true; + }; + } #+END_SRC ** Envrc #+BEGIN_SRC nix { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - envrc - ]; + programs.emacs.init.usePackage.envrc = { + enable = true; + hook = ["(after-init . envrc-global-mode)"]; + custom.envrc-show-summary-in-minibuffer = false; }; } #+END_SRC -#+BEGIN_SRC elisp - (use-package envrc - :hook (after-init . envrc-global-mode) - :custom - (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 - ]; + programs.emacs.init.usePackage = { + magit = { + enable = true; + bind = { + "s-g" = "magit-status"; + "C-x g" = "magit-status"; + "C-c f" = "magit-status"; + }; + custom = { + magit-save-repository-buffers = "'dontask"; + magit-commit-show-diff = false; + }; + 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) + ''; + }; + + forge = { + enable = true; + 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)) + ''; + }; + + diff-hl = { + enable = true; + hook = [ + "(after-init . global-diff-hl-mode)" + "(magit-post-refresh . diff-hl-magit-post-refresh)" + ]; + }; + + consult-gh = { + enable = true; + command = ["consult-gh"]; + }; + + git-timemachine = { + enable = true; + command = ["git-timemachine"]; + }; }; } #+END_SRC -#+BEGIN_SRC elisp - (use-package magit - :bind (("s-g" . magit-status) - ("C-x g" . magit-status) - ("C-c f" . magit-file-dispatch)) - :custom - (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)) - - (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))) - - (use-package diff-hl - :hook ((after-init . global-diff-hl-mode) - (magit-post-refresh . diff-hl-magit-post-refresh))) -#+END_SRC - ** Vertico #+BEGIN_SRC nix { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - cape - consult - corfu - embark-consult - marginalia - orderless - vertico - ]; + programs.emacs.init.usePackage = { + consult = { + enable = true; + bind = { + "s-b" = "consult-project-buffer"; + "C-c h" = "consult-history"; + "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 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"; + }; + custom = { + consult-narrow-key = ''"<"''; + }; + config = '' + (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)) + ''; + }; + + consult-eglot.enable = true; + + xref = { + enable = true; + defer = true; + custom = { + xref-show-xrefs-function = "'consult-xref"; + xref-show-definitions-function = "'consult-xref"; + }; + }; + + embark-consult = { + enable = true; + defer = true; + }; + + orderless = { + enable = true; + defer = true; + }; + + vertico = { + enable = true; + hook = ["after-init"]; + custom = { + vertico-count = 25; + }; + }; + + minibuffer = { + enable = true; + custom = { + completion-styles = "'(orderless basic)"; + }; + }; + + marginalia = { + enable = true; + hook = ["after-init"]; + }; + + corfu = { + enable = true; + hook = ["(after-init . global-corfu-mode)"]; + custom = { + corfu-cycle = true; + corfu-popupinfo-mode = false; + corfu-quit-at-boundary = false; + corfu-on-exact-match = "'show"; + }; + }; + + cape = { + enable = true; + defer = true; + init = "(add-hook 'completion-at-point-functions 'cape-file)"; + }; }; } #+END_SRC -#+BEGIN_SRC elisp - (use-package consult - :bind - (;; ("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) - - ("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 - 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))) - - (use-package vertico - :hook after-init - :custom - (vertico-count 25)) - - (setopt completion-styles '(orderless basic)) - - (use-package marginalia - :hook after-init) - - (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)) - ;; (setq completion-in-region-function #'consult-completion-in-region) - - (use-package cape - :defer t - :init - (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 - ]; + programs.emacs.init.usePackage = { + org = { + enable = true; + 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 = false; + org-link-keep-stored-after-insertion = true; + 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 = true; + org-enforce-todo-dependencies = true; + org-agenda-dim-blocked-tasks = "'invisible"; + }; + config = '' + (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") + ''; + }; + + simple = { + enable = true; + hook = ["(org-mode . visual-line-mode)"]; + }; + + org-modern = { + enable = true; + hook = ["org-mode"]; + }; + + org-roam = { + enable = true; + defer = true; + }; + + verb = { + enable = true; + defer = true; + }; + + visual-fill-column = { + enable = true; + defer = true; + }; }; } #+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")) - - +#+BEGIN_SRC elisp :tangle nil :noweb nil ;; (require 'org-roam) ;; (bind-keys ("C-c n l" . org-roam-buffer-toggle) ;; ("C-c n f" . org-roam-node-find) @@ -2158,84 +2384,79 @@ for completion. That makes it really annoying to use; don't use yasnippet with e ;; (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 *** Presentations +:PROPERTIES: +:header-args:elisp: :tangle no :noweb-ref emacs-org-present-config +:END: + ~org-present~ is a cool little package. It lets you give presentations with org mode. Each top-level heading turns into a slide. When you switch to present mode, ~org-present~ removes all other windows, narrows the buffer to only one top-level heading at a time, and exposes keys to quickly navigate forward / backward between slides. It also exposes entry / exit hooks. Those hooks allow you to add custom styling to present mode. For example, you could make all the text bigger. -#+begin_src elisp - (use-package org-present - :commands (org-present) - :config - (require 'face-remap) - (require 'visual-fill-column) - - (defun my/org-present-start () - <> - ) - - (defun my/org-present-end () - <> - ) - - (add-hook 'org-present-mode-hook 'my/org-present-start) - (add-hook 'org-present-mode-quit-hook 'my/org-present-end)) +#+begin_src nix + { + programs.emacs.init.usePackage.org-present = { + enable = true; + command = ["org-present"]; + init = '' + <> + ''; + config = '' + <> + ''; + }; + } #+end_src Let's customize the window appearance when presenting. First, we'll make fonts larger, by multiplying them by a constant -factor. We'll use ~face-remap~ for that. When ~face-remap~ changes a face, it returns a "cookie" that can be used to restore the face to its old size. We'll store those cookies in a buffer-local variable. - -#+begin_src elisp - (defvar-local my/org-present-face-remapping-cookies nil) -#+end_src +factor. We'll use ~face-remap~ for that. When ~face-remap~ changes a face, it returns a "cookie" that can be used to +restore the face to its old size. We'll store those cookies in a buffer-local variable. When we enter present mode, we'll enlarge the fonts. -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-start - (setq-local my/org-present-face-remapping-cookies nil) - (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)) +#+begin_src elisp :noweb-ref emacs-org-present-init + (defvar-local my/org-present-face-remapping-cookies nil) #+end_src -And when we leave, we'll restore the old faces. - -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-end - (dolist (cookie my/org-present-face-remapping-cookies) - (face-remap-remove-relative cookie)) +#+begin_src elisp + (add-hook 'org-present-mode-hook + (lambda () + (require 'face-remap) + (setq-local my/org-present-face-remapping-cookies nil) + (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)))) + (add-hook 'org-present-mode-quit-hook + (lambda () + (dolist (cookie my/org-present-face-remapping-cookies) + (face-remap-remove-relative cookie)))) #+end_src Let's add some empty space at the top as a margin. This is kind of a hack, but :shrug:. -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-start - (setq header-line-format " ") -#+end_src - -And we'll clean it up when we're done. - -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-end - (setq header-line-format nil) +#+begin_src elisp + (add-hook 'org-present-mode-hook + (lambda () (setq header-line-format " "))) + (add-hook 'org-present-mode-quit-hook + (lambda () (setq header-line-format nil))) #+end_src To make this look more like a "real" presentation, we want to center the text in the window. We'll use @@ -2246,37 +2467,36 @@ To make this look more like a "real" presentation, we want to center the text in Let's set it up when we enter present mode: -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-start - (visual-fill-column-mode 1) - (setq visual-fill-column-center-text t) -#+end_src - -And tear it down when we're done: - -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-end - (visual-fill-column-mode 0) +#+begin_src elisp + (add-hook 'org-present-mode-hook + (lambda () + (visual-fill-column-mode 1) + (setq visual-fill-column-center-text t))) + (add-hook 'org-present-mode-quit-hook + (lambda () + (visual-fill-column-mode 0))) #+end_src We want to display inline images, of course. -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-start - (org-display-inline-images) -#+end_src - -And stop when we're done. - -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-end - (org-remove-inline-images) +#+begin_src elisp + (add-hook 'org-present-mode-hook + (lambda () + (org-display-inline-images))) + (add-hook 'org-present-mode-quit-hook + (lambda () + (org-remove-inline-images))) #+end_src And finally, we want to hide the cursor when presenting. -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-start - (org-present-hide-cursor) -#+end_src - -#+begin_src elisp :tangle nil :noweb-ref emacs-org-present-end - (org-present-show-cursor) +#+begin_src elisp + (add-hook 'org-present-mode-hook + (lambda () + (org-present-hide-cursor))) + (add-hook 'org-present-mode-quit-hook + (lambda () + (org-present-show-cursor))) #+end_src ** Terminal @@ -2295,10 +2515,8 @@ And finally, we want to hide the cursor when presenting. ** Ledger #+BEGIN_SRC nix { - programs.emacs = { - extraPackages = epkgs: with epkgs; [ - ledger-mode - ]; + programs.emacs.init.usePackage = { + ledger-mode.enable = true; }; } #+END_SRC