Compare commits

..

14 Commits

Author SHA1 Message Date
tomatocream 49b64da74d feat(ai): add gemini-cli integration with doom leader bindings 2026-03-21 12:27:00 +08:00
tomatocream 0c061e37ab fix(ai): unset CLAUDECODE to allow nested sessions + add justl.el 2026-03-21 12:23:07 +08:00
tomatocream 4be0e63fac set to use eat add key bindings 2026-03-15 13:52:36 +08:00
tomatocream cb6d55b456 Update Claude terminal and theme configuration.
Switch Claude Code usage toward EAT-related setup and simplify theme loading to doom-one while adding the emacs-eat package declaration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-06 02:19:39 +08:00
tomatocream c021bc8452 Update Claude and theme configuration.
Configure claude-code/claudemacs runtime settings, switch theme setup to ewal-doom-one, and remove obsolete GPTEL setup docs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-05 21:16:11 +08:00
tomatocream 298a9913b5 a 2025-11-22 20:13:14 +08:00
tomatocream 62dd22b544 update 2025-11-22 14:04:34 +08:00
tomatocream 5000e00fb6 a 2025-11-22 14:01:07 +08:00
tomatocream 5d369dd3b5 updates 2025-11-22 13:59:15 +08:00
tomatocream fafe8f4ab5 update 2025-11-22 13:52:03 +08:00
tomatocream c23a1ba64a aeuaeo 2025-11-07 01:26:43 +08:00
tomatocream e1b3fe7244 add 2025-10-19 22:16:22 +08:00
tomatocream 48f26adf81 update add claude code 2025-10-19 21:57:36 +08:00
tomatocream 788b862e5b factor 2025-10-19 21:54:29 +08:00
13 changed files with 832 additions and 321 deletions
+20
View File
@@ -0,0 +1,20 @@
# Emacs temporary files
*~
\#*\#
.#*
*.elc
# Doom/Package generated
auto-save-list/
tramp/
# AI Assistant folders
.claude/
.opencode/
# MacOS (just in case)
.DS_Store
+337
View File
@@ -0,0 +1,337 @@
;;; 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)))
+15 -317
View File
@@ -1,321 +1,19 @@
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
(setq doom-theme 'doom-one)
;; Load modular configuration files
(load! "security")
(load! "core")
(load! "development")
(load! "org")
(load! "ai")
(load! "inbox")
(load! "metrics")
(load! "theme")
(setq display-line-numbers-type t)
;; Performance tweaks
(setq gc-cons-threshold 100000000 ; Increase GC threshold to 100MB
gc-cons-percentage 0.6)
(run-with-idle-timer 5 t #'garbage-collect)
(setq org-directory "~/org/")
(defun load-if-exists (f)
(if (file-exists-p (expand-file-name f))
(load-file (expand-file-name f))))
(load-if-exists "~/.config/doom/secrets.el")
(after! epa-file
(setq epa-file-encrypt-to "dingfengwong@gmail.com"))
(after! auth-source
(setq auth-sources '("~/.authinfo.gpg")))
(setq epa-file-cache-passphrase-for-symmetric-encryption t)
(use-package! super-save
:config
(super-save-mode +1)
(setq super-save-auto-save-when-idle t)
(setq super-save-idle-duration 1))
;; Search bindings for consult-line
(map!
:n "C-s" #'consult-line
:n "M-s" (lambda () (interactive) (consult-line (thing-at-point 'symbol)))
:n "C-S-s" #'consult-line-multi)
(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
))
(after! lsp-haskell
(setq lsp-haskell-plugin-fourmolu-config-path "fourmolu -o -XImportQualifiedPost -o -XOverloadedStrings -o -XTypeApplications -o -XScopedTypeVariables -o -XGADTs -o -XDataKinds -o -XTypeFamilies -o -XFlexibleContexts -o -XFlexibleInstances -o -XMultiParamTypeClasses -o -XRankNTypes -o -XExistentialQuantification")
)
(defun +org--restart-mode-h ()
"Restart `org-mode', but only once."
(remove-hook 'doom-switch-buffer-hook #'+org--restart-mode-h
'local)
(delq! (current-buffer) org-agenda-new-buffers)
(let ((file buffer-file-name)
(old-buffer (current-buffer))
(inhibit-redisplay t)
new-buffer)
(kill-buffer)
(setq new-buffer (find-file file))
(unless (buffer-live-p old-buffer)
(make-indirect-buffer new-buffer old-buffer 'clone))))
(defun +org--restart-mode-h ()
"Restart `org-mode', but only once."
(remove-hook 'doom-switch-buffer-hook #'+org--restart-mode-h
'local)
(delq! (current-buffer) org-agenda-new-buffers)
(let ((file buffer-file-name)
(inhibit-redisplay t))
(kill-buffer)
(find-file file)))
(defun +org--restart-mode-h ()
"Restart `org-mode', but only once."
(remove-hook 'doom-switch-buffer-hook #'+org--restart-mode-h 'local)
(quiet! (org-mode-restart))
(delq! (current-buffer) org-agenda-new-buffers)
(run-hooks 'find-file-hook))
(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)))))
(use-package org
:defer t
:config
(defalias '+org--restart-mode-h #'ignore))
(advice-add #'org-capture :around
(lambda (fun &rest args)
(letf! ((#'+org--restart-mode-h #'ignore))
(apply fun args))))
;;(use-package! org-roam
;; :after org
;; :config
;; :init
;; (setq org-roam-directory "~/org/roam") ; No file-truename needed in Doom
;; :config
;; (advice-remove 'org-roam-db-query '+org-roam-try-init-db-a)
;; (org-roam-db-autosync-mode)
;; (setq org-roam-completion-everywhere t))
; (use-package! org-roam
; :custom
; (org-roam-directory "~/org/roam")
; (org-roam-completion-everywhere t)
; (org-roam-capture-templates
; '(("d" "default" plain
; "%?"
; :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
; "#+title: ${title}\n")
; :unnarrowed t)
; ("r" "reference" plain
; "%?"
; :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
; "#+title: ${title}\n#+filetags: :reference:\n")
; :unnarrowed t)
; ("p" "project" plain
; "\n* Goals\n\n%?\n\n* Tasks\n\n* Notes\n\n"
; :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
; "#+title: ${title}\n#+filetags: :project:\n")
; :unnarrowed t)))
; :config
; (org-roam-setup)
; ;; If using org-roam-protocol
; (require 'org-roam-protocol))
(after! org
(require 'org-re-reveal)
(setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")
(setq org-re-reveal-theme "white")
(setq org-re-reveal-transition "slide"))
(after! org
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("n" . "notes")))
(use-package! anki-editor
:after org
:config
(setq anki-editor-create-decks t)
:bind (:map org-mode-map
("C-c a" . anki-editor-push-notes)))
(use-package! org-anki
:after org
:config
(setq org-anki-default-deck "Default")
:bind (:map org-mode-map
("C-c n" . org-anki-sync-entry)))
(setq-default show-trailing-whitespace 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
(use-package! nix-mode
:mode "\\.nix\\'"
:config
;; 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)
(if (in-nixpkgs-repo-p)
(format-all-buffer 'nixfmt)
(format-all-buffer 'alejandra))
(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"))
(after! org
(use-package! ob-mermaid
:config
(setq ob-mermaid-cli-path "mmdc")) ; Adjust path if needed
;; Add mermaid to org-babel languages
(org-babel-do-load-languages
'org-babel-load-languages
(append org-babel-load-languages
'((mermaid . t)))))
(after! org
(setq-default org-download-image-dir "./images")
(defun my/org-download-image-dir-setup ()
"Create and set org-download-image-dir relative to current org file."
(let* ((current-file-dir (file-name-directory (buffer-file-name)))
(image-dir (expand-file-name "images" current-file-dir)))
(make-directory image-dir t)
(setq-local org-download-image-dir image-dir)))
(add-hook 'org-mode-hook #'my/org-download-image-dir-setup)
(setq org-download-link-format "[[file:%s]]\n")
(setq org-download-timestamp "%Y%m%d-%H%M%S_")
(setq org-download-screenshot-method "maim -s %s")
)
(setq gptel-model 'google/gemini-2.5-flash ;; Default model
gptel-backend
(gptel-make-openai "OpenRouter" ;Any name you want
:host "openrouter.ai"
:endpoint "/api/v1/chat/completions"
:stream t
:key "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" ;can be a function that returns the key
:models '(google/gemini-2.5-flash
google/gemini-2.5-pro
anthropic/claude-sonnet-4
anthropic/claude-sonnet-4.5
)))
;; Keybinding to open inbox.el
(map! "C-c i" #'(lambda () (interactive) (find-file (expand-file-name "inbox.el" doom-user-dir))))
+25
View File
@@ -0,0 +1,25 @@
;;; core.el -*- lexical-binding: t; -*-
(setq doom-theme 'doom-one)
(setq display-line-numbers-type t)
(setq org-directory "~/org/")
(use-package! super-save
:config
(super-save-mode +1)
(setq super-save-auto-save-when-idle t)
(setq super-save-idle-duration 1))
;; Which-key: show keybindings faster
(setq which-key-idle-delay 0.01
which-key-min-display-time 0.1)
;; Search bindings for consult-line
(map!
:n "C-s" #'consult-line
:n "M-s" (lambda () (interactive) (consult-line (thing-at-point 'symbol)))
:n "C-S-s" #'consult-line-multi)
(setq-default show-trailing-whitespace nil)
+15
View File
@@ -0,0 +1,15 @@
;;; -*- lexical-binding: t -*-
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(custom-safe-themes
'("0325a6b5eea7e5febae709dab35ec8648908af12cf2d2b569bedc8da0a3a81c1"
"8a379e7ac3a57e64de672dd744d4730b3bdb88ae328e8106f95cd81cbd44e0b6" default)))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
+151
View File
@@ -0,0 +1,151 @@
;;; 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))
+5
View File
@@ -0,0 +1,5 @@
;;; inbox.el -*- lexical-binding: t; -*-
;; Use this file for new packages and configuration snippets
;; before sorting them into their proper places.
+3 -3
View File
@@ -48,6 +48,7 @@
;;unicode ; extended unicode support for various languages
(vc-gutter +pretty) ; vcs diff in the fringe
vi-tilde-fringe ; fringe tildes to mark beyond EOB
(which-key +popup) ; key binding popups
window-select ; visually switch windows
workspaces ; tab emulation, persistence & separate workspaces
zen ; distraction-free coding or writing
@@ -78,7 +79,7 @@
eshell ; the elisp shell that works everywhere
;;shell ; simple shell REPL for Emacs
;;term ; basic terminal emulator for Emacs
;;vterm ; the best terminal emulation in Emacs
vterm ; the best terminal emulation in Emacs
:checkers
syntax ; tasing you for every semicolon you forget
@@ -150,7 +151,7 @@
;;lua ; one-based indices? one-based indices
markdown ; writing docs for people to ignore
;;nim ; python + lisp at the speed of c
nix ; I hereby declare "nix geht mehr!"
(nix +lsp) ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel
(org +brain
+contacts
@@ -159,7 +160,6 @@
+gnuplot
+hugo
+journal
+jupyter
+link
+noter
+pandoc
+33
View File
@@ -0,0 +1,33 @@
;;; metrics.el -*- lexical-binding: t; -*-
;;; Keyfreq - Track command usage statistics
(use-package! keyfreq
:config
(keyfreq-mode 1)
(keyfreq-autosave-mode 1)
(setq keyfreq-file (concat doom-cache-dir "keyfreq")
keyfreq-file-lock (concat doom-cache-dir "keyfreq.lock"))
;; Optional: Exclude commands you don't want to track (uncomment to use)
;; (setq keyfreq-excluded-commands
;; '(self-insert-command
;; forward-char
;; backward-char
;; previous-line
;; next-line))
)
;;; Command Log Mode - visual log of keystrokes and commands
(use-package! command-log-mode
:config
;; Global mode to track everywhere
;; (global-command-log-mode 1) ; Uncomment if you want it always on (can be noisy)
(map! :leader
(:prefix ("t" . "toggle")
:desc "Toggle command log" "L" #'clm/toggle-command-log-buffer))
)
+135
View File
@@ -0,0 +1,135 @@
;;; org.el -*- lexical-binding: t; -*-
(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))
(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)))))
(use-package org
:defer t
:config
(defalias '+org--restart-mode-h #'ignore))
(advice-add #'org-capture :around
(lambda (fun &rest args)
(letf! ((#'+org--restart-mode-h #'ignore))
(apply fun args))))
(use-package! org-roam
:after org
:custom
(org-roam-directory "~/org/roam")
(org-roam-completion-everywhere t)
(org-roam-capture-templates
'(("d" "default" plain
"%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}\n")
:unnarrowed t)
("r" "reference" plain
"%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}\n#+filetags: :reference:\n")
:unnarrowed t)
("p" "project" plain
"\n* Goals\n\n%?\n\n* Tasks\n\n* Notes\n\n"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}\n#+filetags: :project:\n")
:unnarrowed t)))
:config
(org-roam-db-autosync-mode)
:bind (("C-c n l" . org-roam-buffer-toggle)
("C-c n f" . org-roam-node-find)
("C-c n r" . org-roam-node-random)))
; (use-package! org-roam
; :custom
; (org-roam-directory "~/org/roam")
; (org-roam-completion-everywhere t)
; (org-roam-capture-templates
; '(("d" "default" plain
; "%?"
; :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
; "#+title: ${title}\n")
; :unnarrowed t)
; ("r" "reference" plain
; "%?"
; :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
; "#+title: ${title}\n#+filetags: :reference:\n")
; :unnarrowed t)
; ("p" "project" plain
; "\n* Goals\n\n%?\n\n* Tasks\n\n* Notes\n\n"
; :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
; "#+title: ${title}\n#+filetags: :project:\n")
; :unnarrowed t)))
; :config
; (org-roam-setup)
; ;; If using org-roam-protocol
; (require 'org-roam-protocol))
(after! org
(require 'org-re-reveal)
(setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")
(setq org-re-reveal-theme "white")
(setq org-re-reveal-transition "slide"))
(after! org
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("n" . "notes")))
(use-package! anki-editor
:after org
:config
(setq anki-editor-create-decks t)
:bind (:map org-mode-map
("C-c a" . anki-editor-push-notes)))
(use-package! org-anki
:after org
:config
(setq org-anki-default-deck "Default")
:bind (:map org-mode-map
("C-c n" . org-anki-sync-entry)))
(after! org
(use-package! ob-mermaid
:config
(setq ob-mermaid-cli-path "mmdc")) ; Adjust path if needed
;; Add mermaid to org-babel languages
(org-babel-do-load-languages
'org-babel-load-languages
(append org-babel-load-languages
'((mermaid . t)))))
(after! org
(setq-default org-download-image-dir "./images")
(defun my/org-download-image-dir-setup ()
"Create and set org-download-image-dir relative to current org file."
(let ((filename (buffer-file-name)))
(when filename
(let* ((current-file-dir (file-name-directory filename))
(image-dir (expand-file-name "images" current-file-dir)))
(make-directory image-dir t)
(setq-local org-download-image-dir image-dir)))))
(add-hook 'org-mode-hook #'my/org-download-image-dir-setup)
(setq org-download-link-format "[[file:%s]]\n")
(setq org-download-timestamp "%Y%m%d-%H%M%S_")
(setq org-download-screenshot-method "maim -s %s")
)
+54 -1
View File
@@ -1,10 +1,21 @@
;; -*- no-byte-compile: t; -*-
;;; $DOOMDIR/packages.el
;; -*- no-byte-compile: t; -*-
(package! super-save)
;; gptel and extensions
(package! gptel)
;; gptel extensions
(package! gptel-quick
:recipe (:host github :repo "karthink/gptel-quick"))
(package! gptel-extensions
:recipe (:host github :repo "kamushadenes/gptel-extensions.el"))
(package! gptel-autocomplete
:recipe (:host github :repo "JDNdeveloper/gptel-autocomplete"))
(package! org-re-reveal)
(package! copilot
@@ -29,3 +40,45 @@
(package! tree-sitter-langs)
(package! ob-mermaid)
;; Claude Code integration
(package! inheritenv
:recipe (:host github :repo "purcell/inheritenv"))
(package! consult)
(package! popup)
(package! claude-code
:recipe (:host github :repo "stevemolitor/claude-code.el" :branch "main" :depth 1
:files ("*.el" (:exclude "images/*"))))
(package! gemini-cli
:recipe (:host github :repo "linchen2chris/gemini-cli.el" :branch "main"
:files ("*.el" (:exclude "demo.gif"))))
(package! claudemacs
:recipe (:host github :repo "cpoile/claudemacs"))
;; Terminal emulator
(package! eat
:recipe (:host codeberg
:repo "akib/emacs-eat"
:files ("*.el" ("term" "term/*.el") "*.texi"
"*.ti" ("terminfo/e" "terminfo/e/*")
("terminfo/65" "terminfo/65/*")
("integration" "integration/*")
(:exclude ".dir-locals.el" "*-tests.el"))))
;; Justfile mode
(package! justl :recipe (:host github :repo "psibi/justl.el"))
;; Usage metrics and key tracking
(package! keyfreq)
(package! command-log-mode)
;; Theme
(package! base16-theme)
(package! ewal)
(package! ewal-doom-themes
:recipe (:host github :repo "cyruseuros/ewal" :files ("doom-themes/*.el")))
+15
View File
@@ -0,0 +1,15 @@
;;; security.el -*- lexical-binding: t; -*-
(defun load-if-exists (f)
(if (file-exists-p (expand-file-name f))
(load-file (expand-file-name f))))
(load-if-exists "~/.config/doom/secrets.el")
(after! epa-file
(setq epa-file-encrypt-to "dingfengwong@gmail.com"))
(after! auth-source
(setq auth-sources '("~/.authinfo.gpg")))
(setq epa-file-cache-passphrase-for-symmetric-encryption t)
+24
View File
@@ -0,0 +1,24 @@
;;; theme.el -*- lexical-binding: t; -*-
;; Configure ewal
(use-package! ewal
:init
(setq ewal-use-built-in-always-p nil
ewal-use-built-in-on-failure-p t
ewal-built-in-palette "sexy-material"))
(use-package! ewal-doom-themes
:config
;; (load-theme 'ewal-doom-one t)
;;(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)