diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README.md | 85 | ||||
| -rw-r--r-- | init.el | 348 |
3 files changed, 234 insertions, 200 deletions
@@ -14,3 +14,4 @@ claude-code-context.json /.agent-shell/ /agent/ /tramp +/tree-sitter/ @@ -13,72 +13,94 @@ Personal Emacs configuration focused on simplicity and modern tooling while resp ## Structure ``` -~/.emacs.d/ -├── init.el # Main configuration file -├── elisp/ # Custom configuration modules -│ ├── c-init.el # C/C++ configuration -│ └── ansible-init.el # Ansible/YAML configuration -└── README.md # This file +~/.config/emacs/ +├── init.el # Main configuration file +├── elisp/ # Custom configuration modules +│ ├── c-init.el # C/C++ configuration +│ └── ansible-init.el # Ansible/YAML configuration +├── custom.el # Custom-set variables (gitignored) +└── README.md # This file ``` ## Key Packages ### Completion Framework -- **vertico** - Vertical completion UI +- **vertico** - Vertical minibuffer completion UI - **orderless** - Flexible completion style (space-separated patterns) -- **consult** - Enhanced commands (buffer switching, search, etc.) +- **consult** - Enhanced commands (buffer switching, search, yank-pop) - **marginalia** - Helpful annotations in completion candidates +- **embark** - Contextual actions on completion candidates +- **corfu** - In-buffer completion popup (manual trigger) +- **cape** - completion-at-point extensions (dabbrev, file paths) ### Development -- **company** - Auto-completion -- **flycheck** - Syntax checking -- **jinx** - Fast spell-checking (requires enchant) +- **eglot** - Built-in LSP client (Python, JS/TS, C/C++, YAML, JSON, CSS/SCSS, HTML, Haskell) +- **flymake** - Built-in syntax checking (enabled in all `prog-mode` buffers) +- **jinx** - Fast spell-checking globally enabled in `text-mode`, `prog-mode`, and `conf-mode` (skips code identifiers, only checks comments/strings in code) - **treesit-auto** - Tree-sitter modes with automatic grammar installation - **magit** - Git interface - **forge** - GitHub/GitLab integration for magit - **magit-todos** - Show TODO/FIXME comments in magit status +- **diff-hl** - Inline git diff indicators in fringe (synced with magit) ### Editing - **paredit** - Structured editing for Lisp - **expand-region** - Smart region expansion - **move-text** - Move lines/regions up and down - **visual-regexp** - Visual feedback for regexp replace -- **yasnippet** - Snippet expansion - **emmet-mode** - HTML/CSS abbreviation expansion - **rainbow-delimiters** - Colorize nested delimiters +### Search & Navigation +- **avy** - Jump to visible text by typing target chars +- **rg** - ripgrep results buffer +- **wgrep** - Editable grep/rg results buffers + ### Modes - **markdown-mode** - Markdown editing -- **terraform-mode** - Terraform configuration files -- **yaml-mode** - YAML files -- **json-mode** - JSON files -- **hgignore-mode** - Mercurial ignore files +- **terraform-mode** - Terraform/HCL configuration +- **json-reformat** - JSON pretty-printing +- **ansible-doc** - Ansible module documentation lookup +- **jinja2-mode** - Jinja2 template syntax +- **haskell-mode** - Haskell editing +- Built-in `css-mode` handles `.scss` files (auto-upgraded to `css-ts-mode` via treesit-auto) + +### UI +- **solarized-theme** - Color theme +- **auto-dark** - Switch theme with macOS dark mode +- **helpful** - Better Help buffers with source links +- **which-key** - Built-in popup of key completions ### Utilities - **ibuffer-project** - Group buffers by project - **exec-path-from-shell** - Import shell environment variables -- **rg** - ripgrep integration +- **ghostel** - libghostty terminal emulator +- **claude-code-context** - Send Emacs buffer context to Claude Code (loaded from `~/Dev/code/git/elisp/claude-code-context`) ## Key Bindings ### Custom Bindings +- `C-c b` - Browse URL at point - `C-c c` - Compile - `C-c r` - Recompile - `C-c a` - Align regexp - `C-c g` - Consult ripgrep (search with preview) - `C-c s` - Launch eshell +- `C-c C-l` - claude-code-context prefix (`u` update, `d` diagnostics, `c` clear, `m` toggle mode) - `C-x g` - Magit status - `C-x C-b` - ibuffer (better buffer list) - `C-=` - Expand region +- `C-:` - Avy jump (2 chars) +- `C-,` - Embark act (contextual actions) +- `C-.` - Embark dwim (default action) - `M-p` / `M-n` - Move text up/down -- `M-%` - Visual regexp replace -- `C-M-%` - Visual regexp query replace +- `M-/` - Completion at point (corfu) ### Enhanced Default Bindings - `M-x` - Command execution (enhanced with vertico) -- `C-x b` - Switch buffer (enhanced with consult) +- `C-x b` / `C-x 4 b` / `C-x 5 b` - Switch buffer (enhanced with consult) - `M-y` - Yank from kill ring (enhanced with consult) -- `M-/` - Company completion +- `C-h f` / `C-h v` / `C-h k` / `C-h x` - Help (enhanced with helpful) ## Installation @@ -96,31 +118,24 @@ sudo apt install libenchant-2-dev pkg-config ### Setup -1. Clone this repository: +1. Clone this repository to `~/.config/emacs`: ```bash - git clone [email protected]:lukehoersten/emacs.d.git ~/.emacs.d + git clone [email protected]:lukehoersten/emacs.d.git ~/.config/emacs ``` 2. Launch Emacs - packages will auto-install on first run 3. Jinx spell-checker will compile its native module on first launch (requires enchant) -4. For tree-sitter modes, grammars install automatically when first opening a file +4. Tree-sitter grammars install automatically on first use ## Tree-sitter Support -Tree-sitter provides faster, more accurate syntax highlighting and parsing. Enabled automatically for: -- JavaScript/TypeScript -- JSON -- Python -- And many more (via `treesit-auto`) - -Grammars install automatically on first use. +Tree-sitter provides faster, more accurate syntax highlighting and parsing. Enabled automatically via `treesit-auto` for JavaScript/TypeScript, JSON, Python, YAML, CSS, HTML, C/C++, and many more. Grammars install on first use. ## Customizations Emacs customizations are redirected to `custom.el` (gitignored) to keep `init.el` clean. -The configuration uses `package-require` for explicit package management. ## Updating Packages @@ -131,7 +146,11 @@ M-x package-update-all ## Notes -- Theme: Solarized Light (GUI only) +- Theme: Solarized, switches between light/dark with macOS dark mode (via `auto-dark`) - Font: Inconsolata-12 (GUI only) - Shell: eshell (launched automatically on startup) +- Server: emacs server starts automatically for `emacsclient` - Whitespace cleanup on save enabled globally +- Line numbers enabled globally (exempt: eshell, term, vterm, Messages) +- Rainbow delimiters enabled in all `prog-mode` and `text-mode` buffers +- Auto-fill (line wrap at column 120) enabled in all `text-mode` buffers @@ -40,12 +40,6 @@ (global-set-key (kbd "C-c g") 'consult-ripgrep) ; ripgrep with preview -;;; ediff -(setq-default - ediff-split-window-function 'split-window-horizontally - ediff-window-setup-function 'ediff-setup-windows-plain) - - ;;; MacOS (defvar is-mac (equal system-type 'darwin)) (when is-mac @@ -56,8 +50,8 @@ dired-use-ls-dired nil)) ; macOS ls doesn't support --dired -;;; Xorg -(when window-system +;;; GUI +(when (display-graphic-p) (tool-bar-mode -1) ; remove tool bar (scroll-bar-mode -1) ; remove scroll bar (unless is-mac (menu-bar-mode -1)) ; remove menu bar @@ -72,55 +66,58 @@ (unless package-archive-contents (package-refresh-contents)) -(dolist (package '( - ;; completion - corfu ; completion popup UI (capf-native) - cape ; completion-at-point extensions for corfu - vertico ; vertical minibuffer completion - orderless ; fuzzy/space-separated completion style - consult ; enhanced search and navigation commands - embark ; contextual actions on completion candidates - embark-consult ; embark integration for consult - marginalia ; annotations in completion buffers - - ;; editing - expand-region ; expand selection by semantic units - move-text ; move lines up/down with M-p/M-n - paredit ; structured editing for Lisp - visual-regexp ; visual feedback for regexp replace - - ;; search - avy ; jump to visible text by typing target chars - rg ; ripgrep results buffer - wgrep ; editable grep/rg results buffers - - ;; git - magit ; git interface - forge ; GitHub/GitLab integration for magit - magit-todos ; show TODOs in magit status - diff-hl ; inline git diff indicators in fringe - - ;; UI - rainbow-delimiters ; colorize matching parens by depth - ibuffer-project ; group ibuffer by project - solarized-theme ; color theme - auto-dark ; switch theme with macOS dark mode - helpful ; better Help buffers with source links - - ;; language modes - markdown-mode ; markdown editing - terraform-mode ; Terraform/HCL editing - emmet-mode ; HTML/CSS abbreviation expansion - json-reformat ; JSON pretty-printing - treesit-auto ; auto-install and use tree-sitter modes - ansible-doc ; Ansible module documentation lookup - jinja2-mode ; Jinja2 template syntax - - ;; tools - exec-path-from-shell ; sync shell PATH into Emacs - jinx ; spell checker (libenchant) - ghostel ; libghostty terminal emulator - )) +(dolist + (package + '( + ;; completion + corfu ; completion popup UI (capf-native) + cape ; completion-at-point extensions for corfu + vertico ; vertical minibuffer completion + orderless ; fuzzy/space-separated completion style + consult ; enhanced search and navigation commands + embark ; contextual actions on completion candidates + embark-consult ; embark integration for consult + marginalia ; annotations in completion buffers + + ;; editing + expand-region ; expand selection by semantic units + move-text ; move lines up/down with M-p/M-n + paredit ; structured editing for Lisp + visual-regexp ; visual feedback for regexp replace + + ;; search + avy ; jump to visible text by typing target chars + rg ; ripgrep results buffer + wgrep ; editable grep/rg results buffers + + ;; git + magit ; git interface + forge ; GitHub/GitLab integration for magit + magit-todos ; show TODOs in magit status + diff-hl ; inline git diff indicators in fringe + + ;; UI + rainbow-delimiters ; colorize matching parens by depth + ibuffer-project ; group ibuffer by project + solarized-theme ; color theme + auto-dark ; switch theme with macOS dark mode + helpful ; better Help buffers with source links + + ;; language modes + markdown-mode ; markdown editing + terraform-mode ; Terraform/HCL editing + emmet-mode ; HTML/CSS abbreviation expansion + json-reformat ; JSON pretty-printing + treesit-auto ; auto-install and use tree-sitter modes + ansible-doc ; Ansible module documentation lookup + jinja2-mode ; Jinja2 template syntax + haskell-mode ; Haskell editing + + ;; tools + exec-path-from-shell ; sync shell PATH into Emacs + jinx ; spell checker (libenchant) + ghostel ; libghostty terminal emulator + )) (unless (package-installed-p package) (package-install package))) @@ -130,15 +127,48 @@ (require 'ansible-init) -;;; claude-code-context -(add-to-list 'load-path "~/Dev/code/git/elisp/claude-code-context") -(require 'claude-code-context) -(claude-code-context-mode 1) +;;; jinx (spell checking) +(setq-default jinx-languages "en_US") ; LANG default isn't reliably picked up +(global-jinx-mode t) ; auto-enable in text-mode, prog-mode, conf-mode -;;; text-mode -(add-hook 'fundamental-mode-hook 'jinx-mode) ; spellcheck text -(add-hook 'fundamental-mode-hook 'turn-on-auto-fill) ; autofill text +;;; line numbers +(global-display-line-numbers-mode t) + + +;;; prog-mode - applies to all programming-based modes +(add-hook 'prog-mode-hook 'rainbow-delimiters-mode) +(add-hook 'prog-mode-hook 'flymake-mode) ; linting/diagnostics + + +;;; text-mode - applies to all text-based modes +(add-hook 'text-mode-hook 'rainbow-delimiters-mode) +(add-hook 'text-mode-hook 'turn-on-auto-fill) ; wrap prose at fill-column + + +;;; emacs-lisp-mode +(add-hook 'emacs-lisp-mode-hook 'enable-paredit-mode) + + +;;; markdown-mode +(setq-default markdown-command "pandoc -f gfm") + + +;;; html-mode +(add-to-list 'auto-mode-alist '("\\.tpl\\'" . html-mode)) +(add-hook 'html-mode-hook 'emmet-mode) + + +;;; css-mode (also handles .scss via treesit-auto upgrade to css-ts-mode) +(add-to-list 'auto-mode-alist '("\\.scss\\'" . css-mode)) + + +;;; org-mode +(with-eval-after-load 'org + (define-key org-mode-map (kbd "M-p") 'org-move-item-up) + (define-key org-mode-map (kbd "M-S-p") 'org-move-subtree-up) + (define-key org-mode-map (kbd "M-n") 'org-move-item-down) + (define-key org-mode-map (kbd "M-S-n") 'org-move-subtree-down)) ;;; whitespace-mode @@ -151,41 +181,21 @@ space-before-tab space-after-tab)) -;;; org-mode -(add-hook - 'org-mode-hook - (lambda () - (local-set-key (kbd "M-p") 'org-move-item-up) - (local-set-key (kbd "M-S-p") 'org-move-subtree-up) - (local-set-key (kbd "M-n") 'org-move-item-down) - (local-set-key (kbd "M-S-n") 'org-move-subtree-down))) - - ;;; ibuffer (global-set-key (kbd "C-x C-b") 'ibuffer) ; better buffer browser -(add-hook 'ibuffer-hook - (lambda () - (ibuffer-project-set-filter-groups) - (unless (eq ibuffer-sorting-mode 'project-file-relative) - (ibuffer-do-sort-by-project-file-relative)))) +(add-hook + 'ibuffer-hook + (lambda () + (ibuffer-project-set-filter-groups) + (unless (eq ibuffer-sorting-mode 'project-file-relative) + (ibuffer-do-sort-by-project-file-relative)))) (setq ibuffer-show-empty-filter-groups nil) -;;; emacs server (for emacsclient) -(require 'server) -(unless (server-running-p) - (server-start)) - -;;; shell -(global-set-key (kbd "C-c s") 'eshell) ; start shell -(exec-path-from-shell-initialize) -(exec-path-from-shell-copy-env "PYTHONPATH") -(eshell) -(setq-default tramp-default-method "ssh") -(add-hook 'eshell-mode-hook - (lambda () - (setenv "TERM" "emacs") - (setenv "PAGER" "cat"))) +;;; uniquify +(setq-default + uniquify-buffer-name-style 'post-forward + uniquify-separator ":") ;;; vertico / orderless / consult / marginalia @@ -213,21 +223,27 @@ (add-to-list 'completion-at-point-functions #'cape-file) ; file path completion -;;; which-key (built-in Emacs 30+) -(which-key-mode t) +;;; corfu (completion UI) +(global-corfu-mode t) +(global-set-key (kbd "M-/") 'completion-at-point) -;;; helpful (better Help buffers) -(global-set-key (kbd "C-h f") 'helpful-callable) -(global-set-key (kbd "C-h v") 'helpful-variable) -(global-set-key (kbd "C-h k") 'helpful-key) -(global-set-key (kbd "C-h x") 'helpful-command) +;;; avy +(global-set-key (kbd "C-:") 'avy-goto-char-2) ; jump to visible text by 2 chars + + +;;; expand-region +(global-set-key (kbd "C-=") 'er/expand-region) + + +;;; move-text +(global-set-key (kbd "M-p") 'move-text-up) +(global-set-key (kbd "M-n") 'move-text-down) ;;; magit (with-eval-after-load 'magit-todos (magit-todos-mode t)) ; show TODOs in magit status -(add-hook 'git-commit-mode-hook 'flyspell-mode) ;;; diff-hl (inline git diff in fringe) @@ -236,97 +252,95 @@ (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh) -;;; emacs-lisp-mode -(add-hook 'emacs-lisp-mode-hook 'enable-paredit-mode) - - -;;; corfu (completion UI) -(global-corfu-mode t) -(setq-default - corfu-auto nil ; manual trigger only - corfu-cycle t) ; wrap around candidates -(global-set-key (kbd "M-/") 'completion-at-point) - - -;;; flymake (linting/diagnostics) -(add-hook 'prog-mode-hook 'flymake-mode) ; enable linting for all programming modes - - ;;; eglot (LSP client) (with-eval-after-load 'eglot (setq-default eglot-autoshutdown t ; shutdown server when last buffer is killed eglot-send-changes-idle-time 0.5)) ; debounce for sending changes -(add-hook 'python-ts-mode-hook 'eglot-ensure) -(add-hook 'js-ts-mode-hook 'eglot-ensure) -(add-hook 'typescript-ts-mode-hook 'eglot-ensure) -(add-hook 'tsx-ts-mode-hook 'eglot-ensure) -(add-hook 'c-ts-mode-hook 'eglot-ensure) -(add-hook 'c++-ts-mode-hook 'eglot-ensure) -(add-hook 'yaml-ts-mode-hook 'eglot-ensure) -(add-hook 'json-ts-mode-hook 'eglot-ensure) -(add-hook 'css-ts-mode-hook 'eglot-ensure) -(add-hook 'html-ts-mode-hook 'eglot-ensure) -(add-hook 'scss-mode-hook 'eglot-ensure) -(add-hook 'haskell-mode-hook 'eglot-ensure) +(dolist + (hook + '(python-ts-mode-hook + js-ts-mode-hook + typescript-ts-mode-hook + tsx-ts-mode-hook + c-ts-mode-hook + c++-ts-mode-hook + yaml-ts-mode-hook + json-ts-mode-hook + css-ts-mode-hook + html-ts-mode-hook + haskell-mode-hook)) + (add-hook hook 'eglot-ensure)) -;;; uniquify -(setq-default - uniquify-buffer-name-style 'post-forward - uniquify-separator ":") +;;; treesit-auto (automatically use tree-sitter modes and install grammars) +(require 'treesit-auto) +(setq treesit-auto-install t) +(global-treesit-auto-mode) -;;; color-theme -(setq custom-safe-themes t) -(require 'solarized-theme) -(require 'auto-dark) -(setq auto-dark-allow-osascript t - auto-dark-themes '((solarized-dark) (solarized-light))) -(auto-dark-mode 1) +;;; emacs server (for emacsclient) +(require 'server) +(unless (server-running-p) + (server-start)) -;;; markdown-mode -(add-hook 'markdown-mode-hook 'jinx-mode) -(setq-default markdown-command "pandoc -f gfm") +;;; shell +(global-set-key (kbd "C-c s") 'eshell) ; start shell +(exec-path-from-shell-initialize) +(exec-path-from-shell-copy-env "PYTHONPATH") +(exec-path-from-shell-copy-env "LANG") ; for spell-check and locale-aware tools +(eshell) +(add-hook + 'eshell-mode-hook + (lambda () + (setenv "TERM" "emacs") + (setenv "PAGER" "cat"))) -;;; treesit-auto (automatically use tree-sitter modes and install grammars) -(require 'treesit-auto) -(setq treesit-auto-install t) -(global-treesit-auto-mode) +;;; tramp +(setq-default tramp-default-method "ssh") -;;; html-mode -(add-to-list 'auto-mode-alist '("\\.tpl\\'" . html-mode)) -(add-hook 'html-mode-hook 'emmet-mode) +;;; project.el +(with-eval-after-load 'project + (add-to-list 'project-switch-commands '(ghostel-project "Ghostel" ?s) t) + (add-to-list 'project-switch-commands '(claude-code-ide "Claude" ?c) t)) -;;; line numbers and delimiters for all code and text -(add-hook 'prog-mode-hook 'display-line-numbers-mode) -(add-hook 'prog-mode-hook 'rainbow-delimiters-mode) -(add-hook 'text-mode-hook 'display-line-numbers-mode) -(add-hook 'text-mode-hook 'rainbow-delimiters-mode) +;;; claude-code-context +(add-to-list 'load-path "~/Dev/code/git/elisp/claude-code-context") +(require 'claude-code-context) +(claude-code-context-mode 1) -;;; avy -(global-set-key (kbd "C-:") 'avy-goto-char-2) ; jump to visible text by 2 chars +;;; color-theme +(setq custom-safe-themes + '("2b0fcc7cc9be4c09ec5c75405260a85e41691abb1ee28d29fcd5521e4fca575b" ; solarized-light + "7fea145741b3ca719ae45e6533ad1f49b2a43bf199d9afaee5b6135fd9e6f9b8")) ; solarized-dark +(require 'solarized-theme) +(require 'auto-dark) +(setq auto-dark-allow-osascript t + auto-dark-themes '((solarized-dark) (solarized-light))) +(auto-dark-mode 1) -;;; expand-region -(global-set-key (kbd "C-=") 'er/expand-region) +;;; helpful (better Help buffers) +(global-set-key (kbd "C-h f") 'helpful-callable) +(global-set-key (kbd "C-h v") 'helpful-variable) +(global-set-key (kbd "C-h k") 'helpful-key) +(global-set-key (kbd "C-h x") 'helpful-command) -;;; move-text -(global-set-key (kbd "M-p") 'move-text-up) -(global-set-key (kbd "M-n") 'move-text-down) +;;; which-key (built-in Emacs 30+) +(which-key-mode t) -;;; project.el -(with-eval-after-load 'project - (add-to-list 'project-switch-commands '(ghostel-project "Ghostel" ?s) t) - (add-to-list 'project-switch-commands '(claude-code-ide "Claude" ?c) t)) +;;; ediff +(setq-default + ediff-split-window-function 'split-window-horizontally + ediff-window-setup-function 'ediff-setup-windows-plain) (provide 'init) |
