Compare commits

..

18 Commits

Author SHA1 Message Date
tomatocream c406c0f051 feat: add align-repeat utility function for text alignment 2026-07-05 14:26:41 +08:00
tomatocream 46a54a167f gitignore: add keyfreq.html metrics output 2026-04-04 14:46:42 +08:00
tomatocream 5d31ed089d docs: add AGENTS.md for agentic coding guidelines
Add comprehensive guide for agents working in this Doom Emacs config:
- Build/lint/test commands (doom sync, doom build, doom doctor)
- Emacs Lisp code style guidelines
- Doom macro reference (use-package!, add-hook!, map!, after!, load!)
- Naming conventions and common patterns
- Important workflow notes
2026-04-04 14:45:00 +08:00
tomatocream a14cf7865e docs: add Doom Emacs macros and keywords reference 2026-04-04 14:34:29 +08:00
tomatocream 2b5423a925 fix(snippets): update anki-basic keybinding 2026-04-04 14:17:56 +08:00
tomatocream 7bfdf5c028 feat(inbox): add command-log-mode with toggle binding 2026-04-04 14:11:56 +08:00
tomatocream 95b50b3a10 feat(org): final refinement of anki-basic snippet 2026-03-23 22:52:24 +08:00
tomatocream a06f2f5b18 fix(cc): remove redundant flycheck_cpp and update anki snippets 2026-03-23 22:51:40 +08:00
tomatocream 7d6e9f500b feat(org): add anki-basic and anki-multiline snippets 2026-03-23 22:49:44 +08:00
tomatocream b5bb42cca6 feat(cc): redo c++ configuration with clangd and modularity 2026-03-23 16:43:14 +08:00
tomatocream 7d0b244e22 refactor: modularize ai and development configurations 2026-03-23 15:56:17 +08:00
tomatocream 72726d6108 feat(ai): send quick explanation to *gptel* buffer instead of message 2026-03-23 15:47:13 +08:00
tomatocream 53fb673dae feat(ai): add Emacs-style C-c a bindings for helper functions 2026-03-23 15:40:44 +08:00
tomatocream 20a2f69501 feat(ai): refactor path tag helpers and add absolute version 2026-03-23 15:39:10 +08:00
tomatocream ec119361e5 feat(ai): add my/copy-region-as-path-tag helper and binding 2026-03-23 15:37:06 +08:00
tomatocream 94c9caa171 feat(ai): update all gemini flash models to google/gemini-3-flash-preview 2026-03-23 15:34:41 +08:00
tomatocream 72e1ab8c7e theme: disable ewal packages and restore doom-one 2026-03-23 15:30:28 +08:00
tomatocream f99f7a9751 feat: add gemini-3.flash-preview and disable ewal theme
- ai: include gemini-3.flash-preview in model list
- theme: comment out ewal configuration and default to doom-one
2026-03-21 15:32:17 +08:00
16 changed files with 1192 additions and 505 deletions
+1
View File
@@ -7,6 +7,7 @@
# Doom/Package generated # Doom/Package generated
auto-save-list/ auto-save-list/
tramp/ tramp/
keyfreq.html
# AI Assistant folders # AI Assistant folders
.claude/ .claude/
+308
View File
@@ -0,0 +1,308 @@
# AGENTS.md — Doom Emacs Configuration
This is a Doom Emacs 3 personal configuration at `~/.config/doom/`.
## Repository Structure
```
doom/
├── init.el # Module declarations (doom! macro)
├── config.el # Main config, loads modular files via load!
├── packages.el # Package declarations (package! macro)
├── core.el # Core settings and super-save
├── custom.el # Emacs custom variables (auto-generated)
├── security.el # Security settings
├── org.el # Org-mode configuration
├── metrics.el # Usage tracking (keyfreq)
├── theme.el # Theme configuration
├── inbox.el # Quick capture inbox
├── ai/
│ ├── agents.el # Claude Code & Gemini CLI configuration
│ └── gptel.el # GPTel (ChatGPT) configuration
├── development/
│ ├── lsp.el # LSP/LSP-UI configuration
│ ├── cpp.el # C++ development settings
│ ├── nix.el # Nix development settings
│ └── misc.el # Miscellaneous dev settings
├── docs/
│ └── doom-macros-reference.md # Doom macro documentation
└── snippets/ # Yasnippet snippets
```
## Doom Commands
### Essential Commands
```bash
# Sync config (run after ANY change to init.el, packages.el, or package configs)
doom sync
# Rebuild all packages
doom build
# Diagnose issues
doom doctor
# Update all packages
doom sync -u
# Show verbose output
doom -v sync
# Run in batch mode (for CI/testing)
emacs --batch -l ~/.config/doom/config.el --eval "(message \"test\")"
```
### Package Management
```bash
# Force sync without prompts
doom sync -!
# Clean up orphaned packages
doom sync --gc
# Install packages only (no rebuild)
doom install
```
### Testing Individual Config Files
```bash
# Load a specific config file and check for errors
emacs --batch -l ~/.config/emacs/bin/doom -l config.el -l ai/agents.el --eval "(message \"Loaded successfully\")"
# Check Elisp syntax
emacs --batch --eval "(setq lexical-binding t)" -l ai/agents.el --eval "(message \"Syntax OK\")"
```
## Code Style Guidelines (Emacs Lisp)
### File Headers
Every `.el` file should start with a standard header:
```elisp
;;; filename.el -*- lexical-binding: t; -*-
;;; Commentary:
;;; Description of what this file does
;;; Code:
```
### Lexical Binding
Always use `lexical-binding: t` (note the dash, not underscore in the file-local variable).
```elisp
;;; init.el -*- lexical-binding: t; -*-
```
### Naming Conventions
| Element | Convention | Example |
|---------|------------|---------|
| Private functions | `+prefix--name` | `+org--restart-mode-h` |
| Hook functions | `+module-hook-function-h` | `+org-exclude-agenda-buffers-from-workspace-h` |
| Variables | `+module-variable-name` | `+org--restart-mode-h` |
| Package hooks | `-module-hook-function-h` | `-org-exclude-agenda-buffers-from-workspace-h` |
| Doom modules | `:module` | `:lang`, `:completion` |
| Module flags | `+flag` | `+orderless`, `+lsp` |
| Constants | `SCREAMING-SNAKE` | `MAX-HEIGHT` |
### Keybinding Syntax (map!)
```elisp
;; Basic keybindings
(map! :n "C-s" #'consult-line)
;; With lambda
(map! :n "M-s" (lambda () (interactive) (consult-line (thing-at-point 'symbol))))
;; Leader key with prefix
(map! :leader
(:prefix ("C" . "claude-code")
:desc "Start Claude" "c" #'claude-code
:desc "Kill Claude" "k" #'claude-code-kill))
;; State modifiers: :n (normal), :v (visual), :i (insert), :e (emacs)
(map! :nv "C-j" #'something)
```
### use-package! vs use-package
- Use `use-package!` in Doom configs (macro version)
- Use `use-package` when referring to the built-in package.el macro
```elisp
;; Doom configuration
(use-package! org-roam
:after org
:custom
(org-roam-directory "~/org/roam")
:config
(org-roam-db-autosync-mode))
;; Standard Emacs (when needed)
(use-package org
:init
(setq org-todo-keywords '("TODO" "DONE"))
:config
(org-mode 1))
```
### Hook Registration
```elisp
;; Anonymous function with defun inside
(add-hook! 'org-agenda-finalize-hook
(defun +org-exclude-agenda-buffers-from-workspace-h ()
"Don't associate temporary agenda buffers with current workspace."
(when org-agenda-new-buffers
(persp-remove-buffer org-agenda-new-buffers
(get-current-persp)
nil))))
;; :local makes hook buffer-local
(add-hook! :local 'some-mode-hook
(defun my-local-hook ()
(do-something)))
```
### Conditional Loading
```elisp
;; After package loads (preferred in Doom)
(after! lsp-ui
(setq lsp-ui-doc-enable t))
;; With feature check
(when (featurep! :system 'macos)
(setq mac-command-modifier 'meta))
;; After multiple packages
(after! (org org-roam)
(require 'org-roam-protocol))
```
### Defcustom vs Defvar
- Use `defcustom` for user-configurable settings that belong in `custom.el`
- Use `defvar` or `setq` for internal variables
```elisp
;; In packages.el (for user customization)
(setq org-directory "~/org/")
;; Not a defcustom unless it belongs in custom.el
```
### Error Handling
```elisp
;; Use quiet! to suppress output
(quiet! (org-mode-restart))
;; Use fn! or #' for function references
(advice-add #'org-capture :around
(lambda (fun &rest args)
(letf! ((#'+org--restart-mode-h #'ignore))
(apply fun args))))
;; Use ignore when function should do nothing
(defalias '+org--restart-mode-h #'ignore)
```
### Package Recipes (packages.el)
```elisp
;; Simple package from Melpa
(package! super-save)
;; GitHub recipe with custom files
(package! copilot
:recipe (:host github :repo "copilot-emacs/copilot.el" :files ("*.el")))
;; GitHub with exclusion patterns
(package! eat
:recipe (:host codeberg
:repo "akib/emacs-eat"
:files ("*.el" ("term" "term/*.el") "*.texi"
"*.ti" ("terminfo/e" "terminfo/e/*")
(:exclude ".dir-locals.el" "*-tests.el"))))
;; Disable a package
(package! some-package :disable t)
```
## Common Patterns
### Restart/Reload Functions
```elisp
(defun +org--restart-mode-h ()
"Restart org-mode on buffer switch."
(remove-hook 'doom-switch-buffer-hook #'+org--restart-mode-h 'local)
(cl-delete (current-buffer) org-agenda-new-buffers :test 'eq)
(quiet! (org-mode-restart))
(run-hooks 'find-file-hook))
```
### Advice Around Functions
```elisp
(advice-add #'org-capture :around
(lambda (fun &rest args)
(letf! ((#'+org--restart-mode-h #'ignore))
(apply fun args))))
```
### Buffer Display Configuration
```elisp
(add-to-list 'display-buffer-alist
'("^\\*claude"
(display-buffer-in-side-window)
(side . right)
(window-width . 90)))
```
### Environment Variables
```elisp
;; Unset for subprocess access
(setenv "CLAUDECODE" nil)
;; Set with path
(setenv "PATH" (concat "/usr/local/bin:" (getenv "PATH")))
```
## Doom Module System
The `init.el` uses the `doom!` macro to declare enabled modules:
```elisp
(doom! :category
(module +flag)
:another-category)
```
Modules referenced in this config:
- `:completion` — Vertico, Corfu
- `:ui` — Doom dashboard, treemacs, workspaces
- `:editor` — Evil, format+, multiple-cursors
- `:emacs` — Dired, ibuffer, undo
- `:term` — Eshell, vterm
- `:checkers` — Syntax, spell
- `:tools` — LSP, magit, direnv, docker
- `:lang` — Python, Go, Rust, Org, JavaScript, etc.
- `:config` — Default bindings, smartparens
## Important Notes
1. Run `doom sync` after ANY change to `init.el`, `packages.el`, or `config.el`
2. Use `doom build` to rebuild if byte-compilation errors occur
3. This config uses straight.el for package management (via Doom)
4. Custom variables are auto-generated to `custom.el` — don't edit manually
5. The `load!` macro loads relative to `doom-private-dir` (this config directory)
-337
View File
@@ -1,337 +0,0 @@
;;; ai.el -*- lexical-binding: t; -*-
;;; ============================================================================
;;; gptel Configuration
;;; ============================================================================
;; Core gptel setup
(use-package! gptel
:defer t
:config
;; Default backend: OpenRouter
(setq gptel-model 'google/gemini-3.flash-preview
gptel-backend
(gptel-make-openai "OpenRouter"
:host "openrouter.ai"
:endpoint "/api/v1/chat/completions"
:stream t
:key "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"
:models '(google/gemini-2.5-flash
x-ai/grok-4.1-fast
x-ai/grok-code-fast-1
google/gemini-3-pro-preview
anthropic/claude-sonnet-4.5)))
;; Use org-mode by default for better note-taking
(setq gptel-default-mode 'org-mode)
;; Move cursor to end of response automatically
(add-hook 'gptel-post-response-functions 'gptel-end-of-response)
;; Keybindings - using SPC A for AI (uppercase to avoid conflicts)
(map! :leader
(:prefix ("A" . "AI/gptel")
:desc "gptel chat" "g" #'gptel
:desc "gptel send" "s" #'gptel-send
:desc "gptel menu" "m" #'gptel-menu
:desc "gptel rewrite" "r" #'gptel-rewrite
:desc "Add context" "c" #'gptel-add
:desc "Add file to context" "f" #'gptel-add-file))
;; Additional keybindings in gptel buffer
(map! :map gptel-mode-map
"C-c RET" #'gptel-send
"C-c C-k" #'gptel-abort
"C-c C-c" #'gptel-menu))
;; gptel presets for different tasks
(after! gptel
;; Coding assistant preset
(gptel-make-preset 'coder
:description "Expert coding assistant"
:backend "OpenRouter"
:model 'anthropic/claude-sonnet-4.5
:system "You are an expert coding assistant. Provide high-quality code solutions, refactorings, and explanations. Be concise but thorough. Include error handling and follow best practices.")
;; Code explainer preset
(gptel-make-preset 'explain
:description "Explain code to beginners"
:system "Explain this code to a novice programmer. Use simple language and break down complex concepts.")
;; Refactoring preset
(gptel-make-preset 'refactor
:description "Code refactoring expert"
:model 'anthropic/claude-sonnet-4.5
:system "You are a code refactoring expert. Improve code quality, readability, and performance while maintaining functionality. Explain your changes.")
;; Quick answers preset
(gptel-make-preset 'quick
:description "Quick, concise answers"
:model 'google/gemini-2.5-flash
:system "Provide quick, concise answers. Be direct and to the point.")
;; Documentation writer preset
(gptel-make-preset 'docs
:description "Documentation writer"
:system "Write clear, comprehensive documentation. Include examples and explain edge cases."))
;;; ============================================================================
;;; gptel-quick - Quick lookups and explanations
;;; ============================================================================
(use-package! gptel-quick
:after gptel
:defer t
:config
;; Include context from gptel-add if available
(setq gptel-quick-use-context t)
(map! :leader
(:prefix "A"
:desc "Quick lookup" "q" #'gptel-quick)))
;;; ============================================================================
;;; gptel-extensions - Extra utility functions
;;; ============================================================================
(use-package! gptel-extensions
:after gptel
:defer t
:config
(map! :leader
(:prefix "A"
:desc "Load buffer into session" "l" #'gptel-extensions-load-buffer
:desc "Send whole buffer" "b" #'gptel-extensions-send-whole-buffer
:desc "Refactor region" "R" #'gptel-extensions-refactor)))
;;; ============================================================================
;;; gptel-autocomplete - Inline code completion
;;; ============================================================================
(use-package! gptel-autocomplete
:after gptel
:defer t
:commands (gptel-complete gptel-accept-completion)
:config
;; Configure context size
(setq gptel-autocomplete-before-context-lines 100
gptel-autocomplete-after-context-lines 20
gptel-autocomplete-temperature 0.1)
;; Keybindings for autocomplete
(map! :leader
(:prefix "A"
:desc "Complete at point" "C" #'gptel-complete
:desc "Accept completion" "a" #'gptel-accept-completion))
;; Also add convenient keybindings in prog-mode
(map! :map prog-mode-map
"M-TAB" #'gptel-complete
"C-c TAB" #'gptel-complete
"C-c C-a" #'gptel-accept-completion))
;;; ============================================================================
;;; Optional: Add Ollama for local models (commented out by default)
;;; ============================================================================
;; Uncomment to add Ollama support for local models
;; (after! gptel
;; (gptel-make-ollama "Ollama"
;; :host "localhost:11434"
;; :stream t
;; :models '(qwen2.5-coder:latest
;; mistral:latest
;; llama3.2:latest)))
;; To make Ollama default, uncomment:
;; (setq gptel-model 'qwen2.5-coder:latest
;; gptel-backend (gptel-make-ollama "Ollama"
;; :host "localhost:11434"
;; :stream t
;; :models '(qwen2.5-coder:latest
;; mistral:latest)))
;;; ============================================================================
;;; Helper functions
;;; ============================================================================
(defun my/gptel-quick-explain-region ()
"Quickly explain selected code region."
(interactive)
(when (use-region-p)
(let ((code (buffer-substring-no-properties (region-beginning) (region-end))))
(gptel-request code
:system "Explain this code concisely in 2-3 sentences."
:callback (lambda (response info)
(message "Explanation: %s" response))))))
(defun my/gptel-proofread-region ()
"Proofread and improve selected text."
(interactive)
(when (use-region-p)
(gptel-rewrite-region (region-beginning) (region-end)
"Fix spelling, grammar, and improve clarity. Keep the same tone and style.")))
;; Add keybindings for helper functions
(map! :leader
(:prefix "A"
:desc "Quick explain" "e" #'my/gptel-quick-explain-region
:desc "Proofread text" "p" #'my/gptel-proofread-region))
;;; Claude Code Configuration
(use-package! claude-code
:defer t
:config
;; Unset CLAUDECODE to allow running Claude Code inside Emacs sub-processes
(setenv "CLAUDECODE" nil)
(setq claude-code-program "ccr"
claude-code-program-switches '("code" "--dangerously-skip-permissions"))
;; Use Eat backend
(setq claude-code-terminal-backend 'eat
;; Emacs-style editing in Claude buffer: RET inserts newline, S-RET sends
claude-code-newline-keybinding-style 'shift-return-to-send)
;; Enable global mode
(claude-code-mode 1)
;; Emacs-style prefix key (works without Doom/Evil leader keys)
(global-set-key (kbd "C-c c") claude-code-command-map)
;; Set Doom leader keybindings
(map! :leader
(:prefix ("C" . "claude-code")
:desc "Start Claude" "c" #'claude-code
:desc "Start in directory" "d" #'claude-code-start-in-directory
:desc "Continue conversation" "C" #'claude-code-continue
:desc "Resume session" "R" #'claude-code-resume
:desc "New instance" "i" #'claude-code-new-instance
:desc "Kill Claude" "k" #'claude-code-kill
:desc "Kill all instances" "K" #'claude-code-kill-all
:desc "Send command" "s" #'claude-code-send-command
:desc "Send command with context" "x" #'claude-code-send-command-with-context
:desc "Send region/buffer" "r" #'claude-code-send-region
:desc "Send buffer file" "o" #'claude-code-send-buffer-file
:desc "Fix error at point" "e" #'claude-code-fix-error-at-point
:desc "Toggle window" "t" #'claude-code-toggle
:desc "Switch to buffer" "b" #'claude-code-switch-to-buffer
:desc "Select buffer" "B" #'claude-code-select-buffer
:desc "Toggle read-only" "z" #'claude-code-toggle-read-only-mode
:desc "Cycle mode" "M" #'claude-code-cycle-mode
:desc "Transient menu" "m" #'claude-code-transient
:desc "Slash commands" "/" #'claude-code-slash-commands
:desc "Fork conversation" "f" #'claude-code-fork
:desc "Send return" "y" #'claude-code-send-return
:desc "Send escape" "n" #'claude-code-send-escape
:desc "Send 1" "1" #'claude-code-send-1
:desc "Send 2" "2" #'claude-code-send-2
:desc "Send 3" "3" #'claude-code-send-3))
;; Optional: Configure window display (side window on right)
(add-to-list 'display-buffer-alist
'("^\\*claude"
(display-buffer-in-side-window)
(side . right)
(window-width . 90)))
;; Optional: Enable auto-revert for files modified by Claude
(global-auto-revert-mode 1)
;; Optional: Increase vterm scrollback for long conversations
(add-hook 'claude-code-start-hook
(lambda ()
(when (eq claude-code-terminal-backend 'vterm)
(setq-local vterm-max-scrollback 100000)))))
;;; Gemini CLI Configuration
(use-package! gemini-cli
:defer t
:config
;; Set the Gemini executable
(setq gemini-cli-program "gemini"
gemini-cli-program-switches nil)
;; Use Eat backend
(setq gemini-cli-terminal-backend 'eat
;; Emacs-style editing in Gemini buffer: RET inserts newline, S-RET sends
gemini-cli-newline-keybinding-style 'shift-return-to-send)
;; Enable global mode
(gemini-cli-mode 1)
;; Emacs-style prefix key
(global-set-key (kbd "C-c g") gemini-cli-command-map)
;; Set Doom leader keybindings under SPC G
(map! :leader
(:prefix ("G" . "gemini-cli")
:desc "Start Gemini" "c" #'gemini-cli
:desc "Start in directory" "d" #'gemini-cli-start-in-directory
:desc "Continue conversation" "C" #'gemini-cli-continue
:desc "Resume session" "R" #'gemini-cli-resume
:desc "New instance" "i" #'gemini-cli-new-instance
:desc "Kill Gemini" "k" #'gemini-cli-kill
:desc "Kill all instances" "K" #'gemini-cli-kill-all
:desc "Send command" "s" #'gemini-cli-send-command
:desc "Send command with context" "x" #'gemini-cli-send-command-with-context
:desc "Send region/buffer" "r" #'gemini-cli-send-region
:desc "Send buffer file" "o" #'gemini-cli-send-buffer-file
:desc "Fix error at point" "e" #'gemini-cli-fix-error-at-point
:desc "Toggle window" "t" #'gemini-cli-toggle
:desc "Switch to buffer" "b" #'gemini-cli-switch-to-buffer
:desc "Select buffer" "B" #'gemini-cli-select-buffer
:desc "Toggle read-only" "z" #'gemini-cli-toggle-read-only-mode
:desc "Cycle mode" "M" #'gemini-cli-cycle-mode
:desc "Transient menu" "m" #'gemini-cli-transient
:desc "Slash commands" "/" #'gemini-cli-slash-commands
:desc "Fork conversation" "f" #'gemini-cli-fork
:desc "Send return" "y" #'gemini-cli-send-return
:desc "Send escape" "n" #'gemini-cli-send-escape
:desc "Send 1" "1" #'gemini-cli-send-1
:desc "Send 2" "2" #'gemini-cli-send-2
:desc "Send 3" "3" #'gemini-cli-send-3))
;; Optional: Configure window display (side window on right)
(add-to-list 'display-buffer-alist
'("^\\*gemini"
(display-buffer-in-side-window)
(side . right)
(window-width . 90)))
;; Optional: Enable auto-revert for files modified by Gemini
;; (global-auto-revert-mode 1)
;; Optional: Increase vterm scrollback for long conversations
(add-hook 'gemini-cli-start-hook
(lambda ()
(when (eq gemini-cli-terminal-backend 'vterm)
(setq-local vterm-max-scrollback 100000)))))
;; (use-package! claudemacs
;; :defer t
;; :commands (claudemacs claudemacs-transient-menu)
;; :init
;; (setq claudemacs-program "ccr"
;; claudemacs-program-switches '("code" "--dangerously-skip-permissions")
;; claudemacs-default-tool 'claude
;; claudemacs-prefer-projectile-root t)
;; :config
;; (map! :map prog-mode-map
;; "C-c C-e" #'claudemacs-transient-menu)
;; (map! :map emacs-lisp-mode-map
;; "C-c C-e" #'claudemacs-transient-menu)
;; (map! :map text-mode-map
;; "C-c C-e" #'claudemacs-transient-menu)
;; (after! python
;; (map! :map python-base-mode-map
;; "C-c C-e" #'claudemacs-transient-menu))
;; (after! eat
;; (setq eat-term-scrollback-size 400000)
;; (add-hook 'eat-mode-hook
;; (lambda ()
;; (setq-local show-trailing-whitespace nil))))
;; (map! :leader
;; (:prefix ("C" . "claude-code")
;; :desc "Claudemacs menu" "E" #'claudemacs-transient-menu)))
+133
View File
@@ -0,0 +1,133 @@
;;; ai/agents.el -*- lexical-binding: t; -*-
;;; ============================================================================
;;; Claude Code Configuration
;;; ============================================================================
(use-package! claude-code
:defer t
:config
;; Unset CLAUDECODE to allow running Claude Code inside Emacs sub-processes
(setenv "CLAUDECODE" nil)
(setq claude-code-program "ccr"
claude-code-program-switches '("code" "--dangerously-skip-permissions"))
;; Use Eat backend
(setq claude-code-terminal-backend 'eat
;; Emacs-style editing in Claude buffer: RET inserts newline, S-RET sends
claude-code-newline-keybinding-style 'shift-return-to-send)
;; Enable global mode
(claude-code-mode 1)
;; Emacs-style prefix key (works without Doom/Evil leader keys)
(global-set-key (kbd "C-c c") claude-code-command-map)
;; Set Doom leader keybindings
(map! :leader
(:prefix ("C" . "claude-code")
:desc "Start Claude" "c" #'claude-code
:desc "Start in directory" "d" #'claude-code-start-in-directory
:desc "Continue conversation" "C" #'claude-code-continue
:desc "Resume session" "R" #'claude-code-resume
:desc "New instance" "i" #'claude-code-new-instance
:desc "Kill Claude" "k" #'claude-code-kill
:desc "Kill all instances" "K" #'claude-code-kill-all
:desc "Send command" "s" #'claude-code-send-command
:desc "Send command with context" "x" #'claude-code-send-command-with-context
:desc "Send region/buffer" "r" #'claude-code-send-region
:desc "Send buffer file" "o" #'claude-code-send-buffer-file
:desc "Fix error at point" "e" #'claude-code-fix-error-at-point
:desc "Toggle window" "t" #'claude-code-toggle
:desc "Switch to buffer" "b" #'claude-code-switch-to-buffer
:desc "Select buffer" "B" #'claude-code-select-buffer
:desc "Toggle read-only" "z" #'claude-code-toggle-read-only-mode
:desc "Cycle mode" "M" #'claude-code-cycle-mode
:desc "Transient menu" "m" #'claude-code-transient
:desc "Slash commands" "/" #'claude-code-slash-commands
:desc "Fork conversation" "f" #'claude-code-fork
:desc "Send return" "y" #'claude-code-send-return
:desc "Send escape" "n" #'claude-code-send-escape
:desc "Send 1" "1" #'claude-code-send-1
:desc "Send 2" "2" #'claude-code-send-2
:desc "Send 3" "3" #'claude-code-send-3))
;; Optional: Configure window display (side window on right)
(add-to-list 'display-buffer-alist
'("^\\*claude"
(display-buffer-in-side-window)
(side . right)
(window-width . 90)))
;; Optional: Enable auto-revert for files modified by Claude
(global-auto-revert-mode 1)
;; Optional: Increase vterm scrollback for long conversations
(add-hook 'claude-code-start-hook
(lambda ()
(when (eq claude-code-terminal-backend 'vterm)
(setq-local vterm-max-scrollback 100000)))))
;;; ============================================================================
;;; Gemini CLI Configuration
;;; ============================================================================
(use-package! gemini-cli
:defer t
:config
;; Set the Gemini executable
(setq gemini-cli-program "gemini"
gemini-cli-program-switches nil)
;; Use Eat backend
(setq gemini-cli-terminal-backend 'eat
;; Emacs-style editing in Gemini buffer: RET inserts newline, S-RET sends
gemini-cli-newline-keybinding-style 'shift-return-to-send)
;; Enable global mode
(gemini-cli-mode 1)
;; Emacs-style prefix key
(global-set-key (kbd "C-c g") gemini-cli-command-map)
;; Set Doom leader keybindings under SPC G
(map! :leader
(:prefix ("G" . "gemini-cli")
:desc "Start Gemini" "c" #'gemini-cli
:desc "Start in directory" "d" #'gemini-cli-start-in-directory
:desc "Continue conversation" "C" #'gemini-cli-continue
:desc "Resume session" "R" #'gemini-cli-resume
:desc "New instance" "i" #'gemini-cli-new-instance
:desc "Kill Gemini" "k" #'gemini-cli-kill
:desc "Kill all instances" "K" #'gemini-cli-kill-all
:desc "Send command" "s" #'gemini-cli-send-command
:desc "Send command with context" "x" #'gemini-cli-send-command-with-context
:desc "Send region/buffer" "r" #'gemini-cli-send-region
:desc "Send buffer file" "o" #'gemini-cli-send-buffer-file
:desc "Fix error at point" "e" #'gemini-cli-fix-error-at-point
:desc "Toggle window" "t" #'gemini-cli-toggle
:desc "Switch to buffer" "b" #'gemini-cli-switch-to-buffer
:desc "Select buffer" "B" #'gemini-cli-select-buffer
:desc "Toggle read-only" "z" #'gemini-cli-toggle-read-only-mode
:desc "Cycle mode" "M" #'gemini-cli-cycle-mode
:desc "Transient menu" "m" #'gemini-cli-transient
:desc "Slash commands" "/" #'gemini-cli-slash-commands
:desc "Fork conversation" "f" #'gemini-cli-fork
:desc "Send return" "y" #'gemini-cli-send-return
:desc "Send escape" "n" #'gemini-cli-send-escape
:desc "Send 1" "1" #'gemini-cli-send-1
:desc "Send 2" "2" #'gemini-cli-send-2
:desc "Send 3" "3" #'gemini-cli-send-3))
;; Optional: Configure window display (side window on right)
(add-to-list 'display-buffer-alist
'("^\\*gemini"
(display-buffer-in-side-window)
(side . right)
(window-width . 90)))
;; Optional: Increase vterm scrollback for long conversations
(add-hook 'gemini-cli-start-hook
(lambda ()
(when (eq gemini-cli-terminal-backend 'vterm)
(setq-local vterm-max-scrollback 100000)))))
+205
View File
@@ -0,0 +1,205 @@
;;; ai/gptel.el -*- lexical-binding: t; -*-
;;; ============================================================================
;;; gptel Configuration
;;; ============================================================================
;; Core gptel setup
(use-package! gptel
:defer t
:config
;; Default backend: OpenRouter
(setq gptel-model 'google/gemini-3-flash-preview
gptel-backend
(gptel-make-openai "OpenRouter"
:host "openrouter.ai"
:endpoint "/api/v1/chat/completions"
:stream t
:key "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"
:models '(google/gemini-3-flash-preview
x-ai/grok-4.1-fast
x-ai/grok-code-fast-1
google/gemini-3-pro-preview
anthropic/claude-sonnet-4.5)))
;; Use org-mode by default for better note-taking
(setq gptel-default-mode 'org-mode)
;; Move cursor to end of response automatically
(add-hook 'gptel-post-response-functions 'gptel-end-of-response)
;; Keybindings - using SPC A for AI (uppercase to avoid conflicts)
(map! :leader
(:prefix ("A" . "AI/gptel")
:desc "gptel chat" "g" #'gptel
:desc "gptel send" "s" #'gptel-send
:desc "gptel menu" "m" #'gptel-menu
:desc "gptel rewrite" "r" #'gptel-rewrite
:desc "Add context" "c" #'gptel-add
:desc "Add file to context" "f" #'gptel-add-file))
;; Additional keybindings in gptel buffer
(map! :map gptel-mode-map
"C-c RET" #'gptel-send
"C-c C-k" #'gptel-abort
"C-c C-c" #'gptel-menu))
;; gptel presets for different tasks
(after! gptel
;; Coding assistant preset
(gptel-make-preset 'coder
:description "Expert coding assistant"
:backend "OpenRouter"
:model 'anthropic/claude-sonnet-4.5
:system "You are an expert coding assistant. Provide high-quality code solutions, refactorings, and explanations. Be concise but thorough. Include error handling and follow best practices.")
;; Code explainer preset
(gptel-make-preset 'explain
:description "Explain code to beginners"
:system "Explain this code to a novice programmer. Use simple language and break down complex concepts.")
;; Refactoring preset
(gptel-make-preset 'refactor
:description "Code refactoring expert"
:model 'anthropic/claude-sonnet-4.5
:system "You are a code refactoring expert. Improve code quality, readability, and performance while maintaining functionality. Explain your changes.")
;; Quick answers preset
(gptel-make-preset 'quick
:description "Quick, concise answers"
:model 'google/gemini-3-flash-preview
:system "Provide quick, concise answers. Be direct and to the point.")
;; Documentation writer preset
(gptel-make-preset 'docs
:description "Documentation writer"
:system "Write clear, comprehensive documentation. Include examples and explain edge cases."))
;;; ============================================================================
;;; gptel-quick - Quick lookups and explanations
;;; ============================================================================
(use-package! gptel-quick
:after gptel
:defer t
:config
;; Include context from gptel-add if available
(setq gptel-quick-use-context t)
(map! :leader
(:prefix "A"
:desc "Quick lookup" "q" #'gptel-quick)))
;;; ============================================================================
;;; gptel-extensions - Extra utility functions
;;; ============================================================================
(use-package! gptel-extensions
:after gptel
:defer t
:config
(map! :leader
(:prefix "A"
:desc "Load buffer into session" "l" #'gptel-extensions-load-buffer
:desc "Send whole buffer" "b" #'gptel-extensions-send-whole-buffer
:desc "Refactor region" "R" #'gptel-extensions-refactor)))
;;; ============================================================================
;;; gptel-autocomplete - Inline code completion
;;; ============================================================================
(use-package! gptel-autocomplete
:after gptel
:defer t
:commands (gptel-complete gptel-accept-completion)
:config
;; Configure context size
(setq gptel-autocomplete-before-context-lines 100
gptel-autocomplete-after-context-lines 20
gptel-autocomplete-temperature 0.1)
;; Keybindings for autocomplete
(map! :leader
(:prefix "A"
:desc "Complete at point" "C" #'gptel-complete
:desc "Accept completion" "a" #'gptel-accept-completion))
;; Also add convenient keybindings in prog-mode
(map! :map prog-mode-map
"M-TAB" #'gptel-complete
"C-c TAB" #'gptel-complete
"C-c C-a" #'gptel-accept-completion))
(defun my/gptel-quick-explain-region ()
"Quickly explain selected code region in the gptel buffer."
(interactive)
(when (use-region-p)
(let* ((code (buffer-substring-no-properties (region-beginning) (region-end)))
(buf (get-buffer-create "*gptel*")))
;; Prepare the buffer and insert the query immediately
(with-current-buffer buf
(goto-char (point-max))
(unless (derived-mode-p 'gptel-mode)
(funcall gptel-default-mode)
(gptel-mode 1))
(insert "\n\n--- Code to Explain ---\n\n" code "\n\n### Explanation Request Sent...\n"))
(display-buffer buf)
;; Send the request
(gptel-request
(format "Explain this code concisely in 2-3 sentences:\n\n%s" code)
:system "You are an expert programmer explaining code."
:callback (lambda (response info)
(with-current-buffer (get-buffer "*gptel*")
(goto-char (point-max))
(insert "\n### Code Explanation\n\n" response "\n\n"))
(message "Explanation received in *gptel* buffer."))))))
(defun my/gptel-proofread-region ()
"Proofread and improve selected text."
(interactive)
(when (use-region-p)
(gptel-rewrite-region (region-beginning) (region-end)
"Fix spelling, grammar, and improve clarity. Keep the same tone and style.")))
(defun my--get-region-as-path-tag (path-fn)
"Helper to get path tag using PATH-FN to determine the file path."
(let* ((filename (funcall path-fn))
(start-line (line-number-at-pos (if (use-region-p) (region-beginning) (point))))
(tag (if (use-region-p)
(let* ((end-pos (1- (region-end)))
(end-line (line-number-at-pos (max (region-beginning) end-pos))))
(format "@%s:%d-%d" filename start-line end-line))
(format "@%s:%d" filename start-line))))
tag))
(defun my/copy-region-as-path-tag ()
"Copy the current region as a relative path tag: @path/to/file.ext:start-end"
(interactive)
(let ((tag (my--get-region-as-path-tag
(lambda () (file-relative-name (buffer-file-name) (doom-project-root))))))
(kill-new tag)
(message "Copied: %s" tag)))
(defun my/copy-region-as-path-tag-absolute ()
"Copy the current region as an absolute path tag: @/abs/path/to/file.ext:start-end"
(interactive)
(let ((tag (my--get-region-as-path-tag #'buffer-file-name)))
(kill-new tag)
(message "Copied: %s" tag)))
;; Add keybindings for helper functions
(defvar my-ai-helper-map (make-sparse-keymap)
"Keymap for my AI helper functions.")
(global-set-key (kbd "C-c a") my-ai-helper-map)
(define-key my-ai-helper-map (kbd "e") #'my/gptel-quick-explain-region)
(define-key my-ai-helper-map (kbd "p") #'my/gptel-proofread-region)
(define-key my-ai-helper-map (kbd "t") #'my/copy-region-as-path-tag)
(define-key my-ai-helper-map (kbd "T") #'my/copy-region-as-path-tag-absolute)
(map! :leader
(:prefix "A"
:desc "Quick explain" "e" #'my/gptel-quick-explain-region
:desc "Proofread text" "p" #'my/gptel-proofread-region
:desc "Copy path tag (relative)" "t" #'my/copy-region-as-path-tag
:desc "Copy path tag (absolute)" "T" #'my/copy-region-as-path-tag-absolute))
+6 -2
View File
@@ -3,9 +3,13 @@
;; Load modular configuration files ;; Load modular configuration files
(load! "security") (load! "security")
(load! "core") (load! "core")
(load! "development") (load! "development/lsp")
(load! "development/cpp")
(load! "development/nix")
(load! "development/misc")
(load! "org") (load! "org")
(load! "ai") (load! "ai/gptel")
(load! "ai/agents")
(load! "inbox") (load! "inbox")
(load! "metrics") (load! "metrics")
(load! "theme") (load! "theme")
-151
View File
@@ -1,151 +0,0 @@
;;; development.el -*- lexical-binding: t; -*-
(after! lsp-ui
(setq lsp-ui-doc-include-signature t ;; Show method signatures in docs
lsp-ui-sideline-show-symbol t ;; Show symbols in the sideline
lsp-ui-doc-show-with-cursor t ;; Show docs when cursor is on symbol
lsp-ui-doc-max-height 30 ;; Increase doc height to show more info
lsp-ui-sideline-show-hover t ;; Show hover information in sideline
lsp-ui-doc-enable t ;; Enable documentation
lsp-signature-auto-activate t ;; Show signature help automatically
lsp-signature-render-documentation t)) ;; Include docs in signature help
(after! eldoc
(setq eldoc-echo-area-use-multiline-p t
eldoc-echo-area-prefer-doc-buffer t))
(use-package! corfu-popupinfo
:after corfu
:hook (corfu-mode . corfu-popupinfo-mode)
:custom
(corfu-popupinfo-delay '(0.25 . 0.1)) ; Faster popup display
(corfu-popupinfo-hide nil) ; Don't hide automatically
)
;; Configure Corfu for richer display
(after! corfu
(setq corfu-auto t ; Enable auto completion
corfu-auto-prefix 2 ; Complete after 2 characters
corfu-preview-current t ; Preview current candidate
corfu-show-documentation t ; Show documentation in popup
corfu-doc-max-height 20 ; Max height for doc popup
corfu-doc-delay 0.2 ; Faster doc popup
corfu-quit-no-match t) ; Quit if no match
;; Show more detailed annotations
(setq corfu-show-annotations t
corfu-annotate-max-width 50)
)
;; If using LSP, configure to show more details
(after! lsp-mode
(setq lsp-completion-show-detail t ; Show method signatures
lsp-completion-show-kind t ; Show completion item kind
lsp-completion-show-label-description t ; Show more details in label
))
;; Haskell LSP uses HLS by default in Doom; removed outdated fourmolu config
(use-package! bnf-mode
:mode "\\.bnf\\'"
:config
;; Optional: Add any custom configuration here
(setq bnf-mode-hook
(lambda ()
;; Enable line numbers
(display-line-numbers-mode)
;; Enable auto-pairing of angle brackets
(modify-syntax-entry ?< "(>" bnf-mode-syntax-table)
(modify-syntax-entry ?> ")<" bnf-mode-syntax-table))))
(use-package! ebnf-mode
:mode "\\.ebnf\\'"
:config
;; Optional customizations:
(setq ebnf-mode-indent-offset 4) ; Default is 8 spaces
(setq ebnf-mode-indent-by-production t) ; Align with the '=' in productions
(setq ebnf-mode-comment-char ?\;) ; Use ; for comments
(setq ebnf-mode-eop-char ?\.)) ; Use . for end of production
(use-package! nix-mode
:mode "\\.nix\\'"
:hook (nix-mode . lsp-deferred)
:config
;; Configure nil LSP server
(after! lsp-mode
(add-to-list 'lsp-language-id-configuration '(nix-mode . "nix"))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection "nil")
:major-modes '(nix-mode)
:server-id 'nil-ls
:priority 0)))
;; Function to check if we're in a nixpkgs repository
(defun in-nixpkgs-repo-p ()
(when-let ((root (or (locate-dominating-file default-directory ".git")
(vc-root-dir))))
(or (string-match-p "/nixpkgs/?$" root)
(file-exists-p (expand-file-name "pkgs/top-level/all-packages.nix" root))
(file-exists-p (expand-file-name ".nixpkgs-version.json" root)))))
;; Set up format-all-mode to use our custom formatter selection
(set-formatter! 'nixfmt
'("nixfmt")
:modes '(nix-mode))
(set-formatter! 'alejandra
'("alejandra" "--quiet")
:modes '(nix-mode))
;; Add hooks to handle all formatting scenarios
(add-hook! 'nix-mode-hook
;; Disable LSP formatting
(setq-local +format-with-lsp nil)
;; Set formatter for format-all-mode (+format/buffer)
(setq-local +format-with
(if (in-nixpkgs-repo-p)
'nixfmt
'alejandra))
;; Handle format-on-save
(when (bound-and-true-p format-all-mode)
(format-all-mode -1))
(add-hook! 'before-save-hook :local
(when (eq major-mode 'nix-mode)
(if (in-nixpkgs-repo-p)
(format-all-buffer 'nixfmt)
(format-all-buffer 'alejandra))))
;; Override lsp-format-buffer
(advice-add 'lsp-format-buffer :around
(lambda (orig-fun &rest args)
(if (eq major-mode 'nix-mode)
(let ((fmt (if (in-nixpkgs-repo-p)
(executable-find "nixfmt")
(executable-find "alejandra"))))
(when fmt
(format-all-buffer (intern (file-name-nondirectory fmt)))))
(apply orig-fun args))))))
;; Enable format-on-save globally if desired
(setq +format-on-save-enabled-modes '(nix-mode))
(use-package! llvm-ts-mode
:mode "\\.ll\\'" ; LLVM IR files
:mode "\\.td\\'" ; TableGen files
:hook (llvm-ts-mode . tree-sitter-mode))
(after! tree-sitter
(add-to-list 'tree-sitter-major-mode-language-alist '(llvm-ts-mode . llvm)))
(after! lsp-clangd
(setq lsp-clients-clangd-executable "clangd")
(setq lsp-clients--clangd-default-executable "clangd")
(setq lsp-clangd-binary-path "clangd"))
;; Justfile mode (psibi/justl.el)
(use-package! justl
:config
(map! :n "e" 'justl-exec-recipe))
+43
View File
@@ -0,0 +1,43 @@
;;; development/cpp.el -*- lexical-binding: t; -*-
;; 4. Optional: Fine-tuning Clangd
;; These arguments are passed directly to the clangd binary.
(setq lsp-clients-clangd-args
'("-j=4"
"--background-index"
"--clang-tidy"
"--completion-style=detailed"
"--header-insertion=never"
"--header-insertion-decorators=0"))
(after! lsp-clangd
(set-lsp-priority! 'clangd 2)) ; Ensure clangd is preferred over ccls
;; Formatting for c++
;; This tells Doom to use the LSP server (clangd) for formatting instead of
;; a separate clang-format call via apheleia.
(setq-hook! '(c-mode-hook c++-mode-hook) +format-with-lsp t)
;; Formatting as I type
;; This enables real-time formatting as you type trigger characters like ; or }
(after! lsp-mode
(setq lsp-enable-on-type-formatting t))
;; Ensure cc-mode settings align with clangd/clang-format
;; While clangd handles most things, basic Emacs indentation settings are
;; still useful as a fallback or for initial indentation.
(after! cc-mode
(setq-default c-basic-offset 2)
(c-add-style "doom-cpp"
'("llvm"
(c-basic-offset . 2)
(c-offsets-alist . ((innamespace . 0)
(access-label . -)
(arglist-intro . +)
(arglist-cont-nonempty . +)))))
(add-hook! '(c-mode-hook c++-mode-hook)
(c-set-style "doom-cpp")))
;; Add C++ documentation support
(set-docsets! 'c++-mode "C++")
(set-docsets! 'c-mode "C")
+44
View File
@@ -0,0 +1,44 @@
;;; development/lsp.el -*- lexical-binding: t; -*-
(after! lsp-ui
(setq lsp-ui-doc-include-signature t ;; Show method signatures in docs
lsp-ui-sideline-show-symbol t ;; Show symbols in the sideline
lsp-ui-doc-show-with-cursor t ;; Show docs when cursor is on symbol
lsp-ui-doc-max-height 30 ;; Increase doc height to show more info
lsp-ui-sideline-show-hover t ;; Show hover information in sideline
lsp-ui-doc-enable t ;; Enable documentation
lsp-signature-auto-activate t ;; Show signature help automatically
lsp-signature-render-documentation t)) ;; Include docs in signature help
(after! eldoc
(setq eldoc-echo-area-use-multiline-p t
eldoc-echo-area-prefer-doc-buffer t))
(use-package! corfu-popupinfo
:after corfu
:hook (corfu-mode . corfu-popupinfo-mode)
:custom
(corfu-popupinfo-delay '(0.25 . 0.1)) ; Faster popup display
(corfu-popupinfo-hide nil) ; Don't hide automatically
)
;; Configure Corfu for richer display
(after! corfu
(setq corfu-auto t ; Enable auto completion
corfu-auto-prefix 2 ; Complete after 2 characters
corfu-preview-current t ; Preview current candidate
corfu-show-documentation t ; Show documentation in popup
corfu-doc-max-height 20 ; Max height for doc popup
corfu-doc-delay 0.2 ; Faster doc popup
corfu-quit-no-match t) ; Quit if no match
;; Show more detailed annotations
(setq corfu-show-annotations t
corfu-annotate-max-width 50)
)
(after! lsp-mode
(setq lsp-completion-show-detail t ; Show method signatures
lsp-completion-show-kind t ; Show completion item kind
lsp-completion-show-label-description t ; Show more details in label
))
+39
View File
@@ -0,0 +1,39 @@
;;; development/misc.el -*- lexical-binding: t; -*-
(use-package! bnf-mode
:mode "\\.bnf\\'"
:config
;; Optional: Add any custom configuration here
(setq bnf-mode-hook
(lambda ()
;; Enable line numbers
(display-line-numbers-mode)
;; Enable auto-pairing of angle brackets
(modify-syntax-entry ?< "(>" bnf-mode-syntax-table)
(modify-syntax-entry ?> ")<" bnf-mode-syntax-table))))
(use-package! ebnf-mode
:mode "\\.ebnf\\'"
:config
;; Optional customizations:
(setq ebnf-mode-indent-offset 4) ; Default is 8 spaces
(setq ebnf-mode-indent-by-production t) ; Align with the '=' in productions
(setq ebnf-mode-comment-char ?\;) ; Use ; for comments
(setq ebnf-mode-eop-char ?\.)) ; Use . for end of production
;; Devicetree mode (ZMK keymap, .dts, .dtsi)
(use-package! dts-mode
:mode ("\\.keymap\\'" "\\.dts\\'" "\\.dtsi\\'"))
;; Justfile mode (psibi/justl.el)
(use-package! justl
:config
(map! :n "e" 'justl-exec-recipe))
;; align-repeat: repeatedly align region by a given regexp/character
;; From EmacsWiki: https://www.emacswiki.org/emacs/AlignCommands
(defun align-repeat (start end regexp)
"Repeat alignment with respect to the given regular expression."
(interactive "r\nsAlign regexp: ")
(align-regexp start end
(concat "\\(\\s-*\\)" regexp) 1 1 t))
+73
View File
@@ -0,0 +1,73 @@
;;; development/nix.el -*- lexical-binding: t; -*-
(use-package! nix-mode
:mode "\\.nix\\'"
:hook (nix-mode . lsp-deferred)
:config
;; Configure nil LSP server
(after! lsp-mode
(add-to-list 'lsp-language-id-configuration '(nix-mode . "nix"))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection "nil")
:major-modes '(nix-mode)
:server-id 'nil-ls
:priority 0)))
;; Function to check if we're in a nixpkgs repository
(defun in-nixpkgs-repo-p ()
(when-let ((root (or (locate-dominating-file default-directory ".git")
(vc-root-dir))))
(or (string-match-p "/nixpkgs/?$" root)
(file-exists-p (expand-file-name "pkgs/top-level/all-packages.nix" root))
(file-exists-p (expand-file-name ".nixpkgs-version.json" root)))))
;; Set up format-all-mode to use our custom formatter selection
(set-formatter! 'nixfmt
'("nixfmt")
:modes '(nix-mode))
(set-formatter! 'alejandra
'("alejandra" "--quiet")
:modes '(nix-mode))
;; Add hooks to handle all formatting scenarios
(add-hook! 'nix-mode-hook
;; Disable LSP formatting
(setq-local +format-with-lsp nil)
;; Set formatter for format-all-mode (+format/buffer)
(setq-local +format-with
(if (in-nixpkgs-repo-p)
'nixfmt
'alejandra))
;; Handle format-on-save
(when (bound-and-true-p format-all-mode)
(format-all-mode -1))
(add-hook! 'before-save-hook :local
(when (eq major-mode 'nix-mode)
(if (in-nixpkgs-repo-p)
(format-all-buffer 'nixfmt)
(format-all-buffer 'alejandra))))
;; Override lsp-format-buffer
(advice-add 'lsp-format-buffer :around
(lambda (orig-fun &rest args)
(if (eq major-mode 'nix-mode)
(let ((fmt (if (in-nixpkgs-repo-p)
(executable-find "nixfmt")
(executable-find "alejandra"))))
(when fmt
(format-all-buffer (intern (file-name-nondirectory fmt)))))
(apply orig-fun args))))))
;; Enable format-on-save globally if desired
(setq +format-on-save-enabled-modes '(nix-mode))
(use-package! llvm-ts-mode
:mode "\\.ll\\'" ; LLVM IR files
:mode "\\.td\\'" ; TableGen files
:hook (llvm-ts-mode . tree-sitter-mode))
(after! tree-sitter
(add-to-list 'tree-sitter-major-mode-language-alist '(llvm-ts-mode . llvm)))
+283
View File
@@ -0,0 +1,283 @@
# Doom Emacs Macros and Keywords Reference
This document describes the macros and their keywords used in Doom Emacs 3 configuration.
## Table of Contents
- [`doom!` - Module Declaration](#doom---module-declaration)
- [`package!` - Package Declaration](#package---package-declaration)
- [`use-package!` / `use-package` - Package Configuration](#use-package--use-package---package-configuration)
- [`add-hook!` - Hook Registration](#add-hook---hook-registration)
- [`setq-hook!` - Set Variables in Hooks](#setq-hook---set-variables-in-hooks)
- [`map!` - Keybinding](#map---keybinding)
- [`after!` - Run Code After Package](#after---run-code-after-package)
- [`load!` - Load Sub-config Files](#load---load-sub-config-files)
---
## `doom!` - Module Declaration
Declares enabled modules and their flags in `init.el`.
**Location:** `init.el`
**Syntax:**
```elisp
(doom! :module
(module +flag1 +flag2)
:another-module)
```
**Keywords:** Module names and optional flags (prefixed with `+`).
**Example:**
```elisp
(doom! :completion
(company +orderless)
:lang
(org +brain +crypt)
:tools
lsp)
```
---
## `package!` - Package Declaration
Declares packages to install in `packages.el`.
**Location:** `packages.el`
| Keyword | Description |
|---------|-------------|
| `:recipe` | MELPA-style straight recipe with `:host`, `:repo`, `:branch`, `:files`, etc. |
| `:disable` | Disable the package (`t` to disable) |
| `:built-in` | Mark as built-in (`'prefer`) |
| `:pin` | Pin to a specific commit hash |
| `:local-repo` | Local repository path |
**Example:**
```elisp
(package! super-save)
(package! copilot
:recipe (:host github :repo "copilot-emacs/copilot.el" :files ("*.el")))
(package! org-re-reveal :disable t)
```
---
## `use-package!` / `use-package` - Package Configuration
Configures packages with a declarative DSL.
**Location:** `config.el` or modular config files
| Keyword | Description |
|---------|-------------|
| `:init` | Code to run immediately (before package loads) |
| `:config` | Code to run after package loads |
| `:defer` | Defer loading (boolean or integer seconds) |
| `:after` | Load after another package or packages |
| `:hook` | Hook into specific hooks |
| `:bind` | Define key bindings |
| `:commands` | Autoload commands |
| `:custom` | Set customization variables |
| `:custom-face` | Customize faces |
| `:pre-init` | Run before package's own `:init` |
| `:pre-config` | Run before package's own `:config` |
| `:post-init` | Run after package's own `:init` |
| `:post-config` | Run after package's own `:config` |
**Example:**
```elisp
(use-package! org-roam
:after org
:custom
(org-roam-directory "~/org/roam")
(org-roam-completion-everywhere t)
:config
(org-roam-db-autosync-mode)
:bind (("C-c n l" . org-roam-buffer-toggle)
("C-c n f" . org-roam-node-find)))
```
### Hook Syntax
```elisp
(use-package! some-package
:hook ((prog-mode . some-package-mode)
(org-mode . some-package-mode)))
```
### Bind Syntax with Maps
```elisp
(use-package! anki-editor
:after org
:bind (:map org-mode-map
("C-c a" . anki-editor-push-notes)))
```
---
## `add-hook!` - Hook Registration
Add hooks with inline function definitions.
**Location:** Any config file
| Keyword | Description |
|---------|-------------|
| `:local` | Make the hook local to current buffer |
**Syntax:**
```elisp
(add-hook! HOOK-SPEC
(defun my-hook-function ()
"Description."
(do-something)))
```
**Example:**
```elisp
(add-hook! 'org-agenda-finalize-hook
(defun +org-exclude-agenda-buffers-from-workspace-h ()
"Don't associate temporary agenda buffers with current workspace."
(when (and org-agenda-new-buffers
(bound-and-true-p persp-mode)
(not org-agenda-sticky))
(let (persp-autokill-buffer-on-remove)
(persp-remove-buffer org-agenda-new-buffers
(get-current-persp)
nil)))))
```
---
## `setq-hook!` - Set Variables in Hooks
Set variables specifically for particular mode hooks.
**Syntax:**
```elisp
(setq-hook! MODE-SPEC VARS...)
```
**Example:**
```elisp
(setq-hook! 'python-mode-hook
python-indent-offset 4
python-shell-interpreter "bpython")
```
---
## `map!` - Keybinding
Define key bindings with a declarative syntax.
**Location:** Any config file
| Keyword | State/Scope |
|---------|-------------|
| `:leader` | Normal state leader key (SPC) |
| `:n` | Normal state |
| `:v` | Visual state |
| `:i` | Insert state |
| `:e` | Emacs state |
| `:o` | Operator-pending state |
| `:ic` | Insert+normal (after ESC) |
| `:nv` | Normal+visual combined |
| `:after` | Delay until package loads |
| `:map` | Specific keymap |
| `:prefix` | Key sequence prefix |
| `:desc` | Description for which-key display |
**Syntax:**
```elisp
(map! KEYCOMBO COMMAND
KEYCOMBO COMMAND
...)
(map! :leader
(:prefix ("KEY" . "DESC")
KEYCOMBO COMMAND
...))
```
**Example:**
```elisp
(map! :n "C-s" #'consult-line
:n "M-s" (lambda () (interactive) (consult-line (thing-at-point 'symbol))))
(map! :leader
(:prefix ("C" . "claude-code")
:desc "Start Claude" "c" #'claude-code
:desc "Kill Claude" "k" #'claude-code-kill))
```
---
## `after!` - Run Code After Package
Run code after a package loads (shorthand for `with-eval-after-load`).
**Syntax:**
```elisp
(after! PACKAGE
BODY...)
```
**Example:**
```elisp
(after! org
(require 'org-re-reveal)
(setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"))
```
---
## `load!` - Load Sub-config Files
Load additional configuration files from `config.el`.
**Location:** `config.el`
**Syntax:**
```elisp
(load! "path/to/file") ; Without .el extension
```
**Example:**
```elisp
;; In config.el
(load! "security")
(load! "core")
(load! "org")
(load! "ai/gptel")
```
---
## Summary Table
| Macro | Purpose | Location |
|-------|---------|----------|
| `doom!` | Declare enabled modules | `init.el` |
| `package!` | Declare packages to install | `packages.el` |
| `use-package!` | Configure packages | `config.el` or modular configs |
| `add-hook!` | Register hooks | Any config file |
| `setq-hook!` | Set vars for mode hooks | Any config file |
| `map!` | Define keybindings | Any config file |
| `after!` | Run code after package loads | Any config file |
| `load!` | Load sub-config files | `config.el` |
---
## See Also
- [Doom Emacs Documentation](https://docs.doomemacs.org)
- [Doom Modules Index](https://github.com/doomemacs/doomemacs/blob/master/docs/modules.org)
- [Getting Started Guide](https://github.com/doomemacs/doomemacs/blob/master/docs/getting_started.org)
+32
View File
@@ -3,3 +3,35 @@
;; Use this file for new packages and configuration snippets ;; Use this file for new packages and configuration snippets
;; before sorting them into their proper places. ;; before sorting them into their proper places.
(defun df/copy-file-line-ref ()
"Copy absolute file path with line reference as @/path#Lline or @/path#Lstart-end."
(interactive)
(let* ((file (or (buffer-file-name) (buffer-name)))
(abs-file (if (file-name-absolute-p file) file (expand-file-name file)))
(ref
(if (use-region-p)
(let* ((start (region-beginning))
(end (region-end))
(start-line (line-number-at-pos start))
(end-line (line-number-at-pos (max start (1- end)))))
(format "@%s#L%d-%d" abs-file start-line end-line))
(format "@%s#L%d" abs-file (line-number-at-pos (point))))))
(kill-new ref)
(when (fboundp 'gui-set-selection)
(gui-set-selection 'CLIPBOARD ref))
(message "%s" ref)))
(global-set-key (kbd "M-S w") #'df/copy-file-line-ref)
(defun load-current-file ()
(interactive)
(load (buffer-file-name)))
(global-set-key (kbd "C-c l") #'load-current-file)
(use-package! command-log-mode
:config
(map! "C-c L" #'(lambda () (interactive)
(command-log-mode (if command-log-mode -1 +1))
(when command-log-mode
(switch-to-buffer "*Command Log*")))))
+1 -1
View File
@@ -115,7 +115,7 @@
:lang :lang
;;agda ; types of types of types of types... ;;agda ; types of types of types of types...
;;beancount ; mind the GAAP ;;beancount ; mind the GAAP
(cc +lsp) ; C > C++ == 1 (cc +lsp +tree-sitter) ; C > C++ == 1
;;clojure ; java with a lisp ;;clojure ; java with a lisp
;;common-lisp ; if you've seen one lisp, you've seen them all ;;common-lisp ; if you've seen one lisp, you've seen them all
;;coq ; proofs-as-programs ;;coq ; proofs-as-programs
+14
View File
@@ -0,0 +1,14 @@
# -*- mode: snippet -*-
# name: Anki Basic (Subheadings)
# key: <a
# --
* ${1:Title} :study_card:
:PROPERTIES:
:ANKI_NOTE_TYPE: Basic
:END:
** Front
$2
** Back
$3
+10 -14
View File
@@ -1,24 +1,20 @@
;;; theme.el -*- lexical-binding: t; -*- ;;; theme.el -*- lexical-binding: t; -*-
;; Configure ewal ;; Default Doom Theme
(setq doom-theme 'doom-one)
;; --- Ewal Configuration ---
(use-package! ewal (use-package! ewal
:disabled t
:init :init
(setq ewal-use-built-in-always-p nil (setq ewal-json-file "~/.cache/wal/colors.json"
ewal-use-built-in-always-p nil
ewal-use-built-in-on-failure-p t ewal-use-built-in-on-failure-p t
ewal-built-in-palette "sexy-material")) ewal-built-in-palette "sexy-material"))
(use-package! ewal-doom-themes (use-package! ewal-doom-themes
:disabled t
:config :config
;; (load-theme 'ewal-doom-one t) (load-theme 'ewal-doom-one t)
;;(enable-theme 'ewal-doom-one) (enable-theme 'ewal-doom-one)
) )
;; Set the theme to ewal-doom-one
;; (setq doom-theme 'ewal-doom-one)
(load-theme 'doom-one t)
(setq doom-theme 'doom-one)
;; Previous base16 config (commented out)
;; (setq base16-distinct-fringe-background t
;; base16-highlight-mode-line 'box)
;; (setq doom-theme 'base16-default-dark)