24 KiB
24 KiB
Emacs config
- Prelims
- Org stuff
- Look
- Feel
- editing
- Tools
- language-specific
- Inspiration
Prelims
Workarounds?
(setq max-lisp-eval-depth 10000)
(setq max-specpdl-size 13000)
keep track of recent files
(recentf-mode 1)
start server
(server-start)
use-package compile-time
(eval-when-compile (require 'use-package))
(require 'bind-key)
Custom file
(setq custom-file "~/dotfiles/emacs/emacs-custom.el")
(load custom-file)
Org stuff
Scratch buffer
(setq org-src-preserve-indentation 't)
(setq initial-major-mode 'org-mode)
(setq initial-scratch-message "\
,#+TITLE: Scratch buffer
,#+AUTHOR: Yorick van Pelt
")
config
(setq org-pretty-entities t)
(setq org-startup-indented t)
(setq org-src-fontify-natively t)
(setq org-completion-use-ido t)
(setq org-src-tab-acts-natively t)
(setq org-ellipsis "⬎")
(setq org-log-done t)
(setq org-todo-keywords
'((sequence "TODO(t)" "WAIT" "|" "DONE(d)" "CANCEL")))
(setq org-use-fast-todo-selection t)
(defun org-file-path (filename)
"Return the absolute address of an org file, given its relative name."
(concat (file-name-as-directory org-directory) filename))
(setq org-index-file (org-file-path "life.org"))
(setq org-archive-location
(concat (org-file-path "archive.org") "::* From %s"))
(setq org-agenda-todo-ignore-scheduled t)
l | Today I learned | entry | (file+datetree (org-file-path til.org)) | * %? |
u | link | entry | (file+headline org-index-file check links) | * %^L |
w | Week review | entry | (file+datetree (org-file-path weeklog.org)) | * %? |
t | todo item | entry | (file+headline org-index-file todo) | * TODO %? |
add templates
(add-to-list 'org-structure-template-alist
'("el" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))
(add-to-list 'org-structure-template-alist
'("py" "#+BEGIN_SRC python\n?\n#+END_SRC"))
(org-babel-do-load-languages
'org-babel-load-languages
'((python . t)
))
org-bullets
(use-package org-bullets
:commands org-bullets-mode
:hook org-mode-hook)
Look
Theme
;; used to fix modeline
;; (defvar after-load-theme-hook nil
;; "Hook run after a color theme is loaded using `load-theme'.")
(defun +y-applicable-theme ()
(let ((color-scheme
(intern (with-temp-buffer
(insert-file-contents "~/dotfiles/color-scheme")
(string-trim (buffer-string))))))
(cond ((eq color-scheme 'light) 'latte)
((eq color-scheme 'dark) 'mocha))))
(defun +y-reload-theme (event)
;(message "reload theme called")
(let ((theme (+y-applicable-theme)))
(if (not (eq catppuccin-flavor theme))
(catppuccin-load-flavor theme))))
(use-package catppuccin-theme
:custom (catppuccin-flavor (+y-applicable-theme))
:config (load-theme 'catppuccin t))
;; auto-reload
(use-package filenotify
;:after catppuccin-theme
:config
(file-notify-add-watch "~/dotfiles/color-scheme" '(change) '+y-reload-theme))
Cleaner frames
;; toolbars are disabled in early-init.el
(setq inhibit-startup-screen t)
hl-line
(when window-system (global-hl-line-mode))
(show-paren-mode t)
(set-frame-parameter nil 'alpha-background 95)
(add-to-list 'default-frame-alist '(alpha-background . 95))
live hex color previews
from https://vickychijwani.me/nuggets-from-my-emacs-part-i/
;; CSS color values colored by themselves
;; http://news.ycombinator.com/item?id=873541
(defvar hexcolor-keywords
'(("#[abcdef[:digit:]]+"
(0 (put-text-property
(match-beginning 0)
(match-end 0)
'face (list :background
(match-string-no-properties 0)))))))
(defun hexcolor-add-to-font-lock ()
(font-lock-add-keywords nil hexcolor-keywords))
(add-hook 'css-mode-hook 'hexcolor-add-to-font-lock)
doom-modeline
(use-package hide-mode-line)
(use-package doom-modeline
:hook
(after-init . doom-modeline-mode)
;(after-load-theme . doom-modeline-mode)
(doom-modeline-mode . size-indication-mode)
(doom-modeline-mode . column-number-mode)
:init
(setq doom-modeline-buffer-encoding 'nondefault
doom-modeline-default-eol-type 0
)
:config
(add-hook 'magit-mode-hook
(defun +modeline-hide-in-non-status-buffer-h ()
"Show minimal modeline in magit-status buffer, no modeline elsewhere."
(if (eq major-mode 'magit-status-mode)
(doom-modeline-set-modeline 'magit)
(hide-mode-line-mode))))
)
solaire mode
(use-package solaire-mode
:config
(solaire-global-mode +1))
Feel
(defalias 'yes-or-no-p 'y-or-n-p)
(setq compilation-scroll-output t)
fix escape
; Map escape to cancel (like C-g)...
(define-key isearch-mode-map [escape] 'isearch-abort) ;; isearch
(define-key isearch-mode-map "\e" 'isearch-abort) ;; \e seems to work better for terminals
(global-set-key [escape] 'keyboard-escape-quit) ;; everywhere else
Fix mouse wheel
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1))) ;; one line at a time
(setq mouse-wheel-progressive-speed nil) ;; don't accelerate scrolling
(setq mouse-wheel-follow-mouse 't) ;; scroll window under mouse
(setq scroll-step 1) ;; keyboard scroll one line at a time
(defun sfp-page-down (&optional arg)
(interactive "^P")
(setq this-command 'next-line)
(next-line
(- (window-text-height)
next-screen-context-lines)))
(put 'sfp-page-down 'isearch-scroll t)
(put 'sfp-page-down 'CUA 'move)
(defun sfp-page-up (&optional arg)
(interactive "^P")
(setq this-command 'previous-line)
(previous-line
(- (window-text-height)
next-screen-context-lines)))
(put 'sfp-page-up 'isearch-scroll t)
(put 'sfp-page-up 'CUA 'move)
(setq scroll-error-top-bottom t)
fix c-z
(global-unset-key (kbd "C-z"))
vertico and consult
(use-package vertico
:config
(vertico-mode)
:demand
:bind (:map vertico-map
("<prior>" . vertico-scroll-down)
("<next>" . vertico-scroll-up)))
(use-package vertico-directory
:after vertico
:ensure nil
;; More convenient directory navigation commands
:bind (:map vertico-map
("RET" . vertico-directory-enter)
("/" . vertico-directory-enter)
("DEL" . vertico-directory-delete-char)
("M-DEL" . vertico-directory-delete-word))
;; Tidy shadowed file names
:hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
(use-package marginalia
:config
(marginalia-mode))
(use-package savehist
:config
(savehist-mode))
(use-package consult
:bind (("C-x b" . consult-buffer)
("C-s" . consult-line)))
(use-package embark
:bind (("C-," . embark-act))
:config
(keymap-set embark-identifier-map "t" #'lsp-find-type-definition)
(keymap-set embark-identifier-map "R" #'lsp-rename)
(keymap-set embark-identifier-map "A" #'lsp-execute-code-action)
(keymap-set embark-identifier-map "h" #'lsp-describe-thing-at-point)
(push 'embark--allow-edit
(alist-get 'lsp-rename embark-target-injection-hooks))
(push 'embark--ignore-target
(alist-get 'lsp-execute-code-action embark-target-injection-hooks))
)
(use-package embark-consult
:ensure t
:hook
(embark-collect-mode . consult-preview-at-point-mode))
(use-package orderless
:custom
;; Configure a custom style dispatcher (see the Consult wiki)
(completion-styles '(orderless basic))
(completion-category-defaults nil)
(completion-category-overrides '((file (styles partial-completion)))))
(use-package emacs
:custom
;; Support opening new minibuffers from inside existing minibuffers.
(enable-recursive-minibuffers t)
;; Emacs 28 and newer: Hide commands in M-x which do not work in the current
;; mode. Vertico commands are hidden in normal buffers. This setting is
;; useful beyond Vertico.
(read-extended-command-predicate #'command-completion-default-include-p)
:init
;; Add prompt indicator to `completing-read-multiple'.
;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
(defun crm-indicator (args)
(cons (format "[CRM%s] %s"
(replace-regexp-in-string
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
crm-separator)
(car args))
(cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
;; Do not allow the cursor in the minibuffer prompt
(setq minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))
ggtags
(use-package ggtags
:bind ("M-." . ggtags-find-tag-dwim))
intuitive window resize
;; intuitive window resizing
(defun xor (b1 b2)
(or (and b1 b2)
(and (not b1) (not b2))))
(defun move-border-left-or-right (arg dir)
"General function covering move-border-left and move-border-right. If DIR is
t, then move left, otherwise move right."
(interactive)
(if (null arg) (setq arg 3))
(let ((left-edge (nth 0 (window-edges))))
(if (xor (= left-edge 0) dir)
(shrink-window arg t)
(enlarge-window arg t))))
(defun move-border-up-or-down (arg dir)
"General function covering move-border-up and move-border-down. If DIR is
t, then move up, otherwise move down."
(interactive)
(if (null arg) (setq arg 3))
(let ((top-edge (nth 1 (window-edges))))
(if (xor (= top-edge 0) dir)
(shrink-window arg nil)
(enlarge-window arg nil))))
(defun move-border-left (arg)
(interactive "P")
(move-border-left-or-right arg t))
(defun move-border-right (arg)
(interactive "P")
(move-border-left-or-right arg nil))
(defun move-border-up (arg)
(interactive "P")
(move-border-up-or-down arg t))
(defun move-border-down (arg)
(interactive "P")
(move-border-up-or-down arg nil))
;; keybindings for window resizing
(global-set-key (kbd "C-S-<left>") 'move-border-left)
(global-set-key (kbd "C-S-<right>") 'move-border-right)
(global-set-key (kbd "C-s-<up>") 'move-border-up)
(global-set-key (kbd "C-s-<down>") 'move-border-down)
move-border-down
Terminal
(xterm-mouse-mode 1)
(define-key local-function-key-map "\033[73;5~" [(control return)])
treemacs
(use-package treemacs
:defer t
:init
(setq treemacs-follow-after-init t
treemacs-is-never-other-window t
treemacs-sorting 'alphabetic-case-insensitive-asc
treemacs-indentation 1
treemacs-user-mode-line-format 'none
treemacs-width 28)
:config
(treemacs-fringe-indicator-mode 'always)
(treemacs-git-commit-diff-mode t)
(treemacs-git-mode 'deferred)
(treemacs-project-follow-mode))
(add-hook 'vterm-mode-hook (lambda () (setq-local global-hl-line-mode nil)))
(add-hook 'treemacs-mode-hook #'hide-mode-line-mode)
(add-hook 'treemacs-mode-hook (lambda () (display-line-numbers-mode -1)))
(with-eval-after-load 'treemacs
(define-key treemacs-mode-map [mouse-1] #'treemacs-single-click-expand-action))
;; (use-package kaolin-themes
;; :custom
;; (kaolin-themes-comments-style 'normal)
;; (kaolin-themes-italic-comments t)
;; :config
;; (load-theme 'kaolin-light t))
;; ;; (kaolin-treemacs-theme)
(use-package treemacs-nerd-icons
:defer t
:init
(with-eval-after-load 'lsp-treemacs
(require 'treemacs-nerd-icons))
:config (treemacs-load-theme "nerd-icons"))
(use-package nerd-icons-dired
:hook
(dired-mode . nerd-icons-dired-mode))
(use-package nerd-icons-completion
:after marginalia
:config
(nerd-icons-completion-mode)
(add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))
(use-package treemacs-evil
:defer t
:init
(with-eval-after-load 'treemacs (require 'treemacs-evil)))
;; :config
;; (define-key! evil-treemacs-state-map
;; [return] #'treemacs-RET-action
;; [tab] #'treemacs-TAB-action
;; "TAB" #'treemacs-TAB-action
;; ;; REVIEW Fix #1875 to be consistent with C-w {v,s}, but this should really
;; ;; be considered upstream.
;; "o v" #'treemacs-visit-node-horizontal-split
;; "o s" #'treemacs-visit-node-vertical-split))
(use-package treemacs-magit
:after treemacs magit)
(use-package lsp-treemacs
:after treemacs)
(defun +y-fix-treemacs-face (&optional theme enable)
(dolist (face '(treemacs-root-face
treemacs-git-unmodified-face
treemacs-git-modified-face
treemacs-git-renamed-face
treemacs-git-ignored-face
treemacs-git-untracked-face
treemacs-git-added-face
treemacs-git-conflict-face
treemacs-directory-face
treemacs-directory-collapsed-face
treemacs-file-face
treemacs-tags-face))
(set-face-attribute face nil :family "DejaVuSans" :height 120))
(set-face-attribute 'treemacs-root-face nil :height 140 :weight 'normal :background 'unspecified))
(with-eval-after-load 'lsp-treemacs
(+y-fix-treemacs-face))
(advice-add 'load-theme :after #'+y-fix-treemacs-face)
editing
line numbers
relative
(use-package linum-relative
:commands linum-relative-toggle)
enable globally
(global-display-line-numbers-mode)
direnv
(use-package direnv
:config
(direnv-mode))
autocomplete
(use-package company
:hook (after-init . global-company-mode)
:config
;; use copilot
(delq 'company-preview-if-just-one-frontend company-frontends))
(use-package company-box
;:hook (company-mode . company-box-mode)
)
Indentation
(setq-default indent-tabs-mode nil)
(setq-default tab-width 2) ; or any other preferred value
(defvaralias 'c-basic-offset 'tab-width)
(defvaralias 'cperl-indent-level 'tab-width)
(defun yorick/copilot-tab (arg)
(interactive "P")
(or (copilot-accept-completion)
(company-indent-or-complete-common arg)))
(define-key prog-mode-map (kbd "<tab>") #'yorick/copilot-tab)
smart home key
;; "smart" home, i.e., home toggles b/w 1st non-blank character and 1st column
(defun smart-beginning-of-line ()
"Move point to first non-whitespace character or beginning-of-line."
(interactive "^") ; Use (interactive "^") in Emacs 23 to make shift-select work
(let ((oldpos (point)))
(back-to-indentation)
(and (= oldpos (point))
(beginning-of-line))))
(global-set-key (kbd "C-a") 'smart-beginning-of-line)
mouse jumps
(global-set-key [mouse-8] 'evil-jump-backward)
(global-set-key [mouse-9] 'evil-jump-forward)
git-gutter-fringe
;; (use-package git-gutter-fringe
;; :config (global-git-gutter-mode t))
all-the-icons
(use-package all-the-icons
:commands all-the-icons-insert)
backups
from emacs wiki
(setq vc-make-backup-files t)
(setq
backup-by-copying t ; don't clobber symlinks
backup-directory-alist
'(("." . "~/.emacs.d/.saves")) ; don't litter my fs tree
delete-old-versions t
kept-new-versions 6
kept-old-versions 2
version-control t) ; use versioned backups
Undo-tree
(use-package undo-tree
:init
;; prevent .~undo-tree file pollution
(setq undo-tree-auto-save-history nil)
:config
(global-undo-tree-mode))
Evil
(setq evil-want-C-i-jump nil)
(use-package evil
:config
(evil-mode t)
(evil-set-undo-system 'undo-tree)
(define-key evil-motion-state-map (kbd "<home>") 'smart-beginning-of-line)
;; change cursor based on mode
(add-hook 'evil-insert-state-entry-hook (lambda () (when (not (display-graphic-p)) (send-string-to-terminal "\033[5 q"))))
(add-hook 'evil-normal-state-entry-hook (lambda () (when (not (display-graphic-p)) (send-string-to-terminal "\033[0 q"))))
)
(use-package evil-mc
:config (global-evil-mc-mode 1)
:after evil)
(use-package which-key
:init
(setq which-key-allow-evil-operators t)
(setq which-key-show-operator-state-maps t)
:config
(which-key-mode 1)
(which-key-setup-minibuffer)) ; do I need this?
t
evil-goggles
(use-package evil-goggles
:after evil
:config
(evil-goggles-mode)
(evil-goggles-use-diff-faces))
TODO evil-surround
TODO more evil bindings
follow link with ret
TODO multiple-cursors
crdt
(use-package crdt
:commands (crdt-connect))
copilot
(use-package copilot
:commands (copilot-login copilot-mode)
:bind (:map copilot-mode-map ("TAB" . copilot-accept-completion)))
DONE fix clipboard on wayland
(setq wl-copy-process nil)
(defun wl-copy (text)
(setq wl-copy-process (let (
(default-directory "~")
;(process-environment (frame-parameter nil 'environment))
)
(make-process :name "wl-copy"
:buffer nil
:command '("wl-copy" "-f" "-n")
:noquery t
:connection-type 'pipe)))
(process-send-string wl-copy-process text)
(process-send-eof wl-copy-process))
(defun wl-paste ()
(if (and wl-copy-process (process-live-p wl-copy-process))
nil
(let (
(default-directory "~")
;(process-environment (frame-parameter nil 'environment))
)
(shell-command-to-string "wl-paste -n | tr -d '\r'"))))
(setq interprogram-cut-function 'wl-copy)
(setq interprogram-paste-function 'wl-paste)
wl-paste
Tools
Magit
(use-package magit
:bind (("C-c g" . magit-status)
("C-c C-g l" . magit-log-all)))
(use-package forge
:config
(setq forge-topic-list-limit '(60 . 0))
:after magit)
gpt keys
(defun +y-get-llm-keys (key)
"Read JSON from FILE-PATH, parse it, and return the value associated with KEY."
(let* ((file-path (expand-file-name "~/.config/io.datasette.llm/keys.json"))
(json-object-type 'alist)
(json-key-type 'symbol))
(with-temp-buffer
(insert-file-contents file-path)
(goto-char (point-min))
(alist-get key (json-read)))))
magit-gptcommit
(use-package magit-gptcommit
:demand t
:after magit
:init
(require 'llm-claude)
:custom
(magit-gptcommit-llm-provider
(make-llm-claude
:key (+y-get-llm-keys 'claude)
:chat-model "claude-3-5-sonnet-20240620"))
(llm-warn-on-nonfree nil)
:config
(magit-gptcommit-status-buffer-setup))
gptel
(use-package gptel
:commands gptel gptel-mode
:config
(setq
gptel-api-key (lambda () (+y-get-llm-keys 'openai))
gptel-model "claude-3-5-sonnet-20240620"
gptel-backend (gptel-make-anthropic "Claude"
:stream t
:key (lambda () (+y-get-llm-keys 'claude))))
(gptel-make-gemini "Gemini"
:stream t
:key (lambda () (+y-get-llm-keys 'gemini)))
)
weechat
(use-package weechat
:commands weechat-connect
:config
(setq weechat-more-lines-amount 100)
(setq weechat-host-default "pennyworth.yori.cc")
(setq weechat-mode-default "ssh -W localhost:%p %h")
(setq weechat-modules '(weechat-button weechat-complete weechat-notifications)))
notmuch
(use-package notmuch
:bind (("C-c n" . notmuch)))
term
(add-hook 'term-mode-hook #'hide-mode-line-mode)
(defun +yorick-term-hscroll-0 ()
(setq hscroll-margin 0)
(display-line-numbers-mode -1))
(add-hook 'term-mode-hook #'+yorick-term-hscroll-0)
(add-hook 'vterm-mode-hook (lambda () (setq-local global-hl-line-mode nil)))
(use-package vterm
:hook (vterm-mode . +yorick-term-hscroll-0)
:config
(setq vterm-timer-delay 0.03)
(push (list "find-file-other-window"
'find-file-other-window)
vterm-eval-cmds)
)
(defun evil-collection-vterm-escape-stay ()
"Go back to normal state but don't move
cursor backwards. Moving cursor backwards is the default vim behavior but it is
not appropriate in some cases like terminals."
(setq-local evil-move-cursor-back nil))
(add-hook 'vterm-mode-hook #'evil-collection-vterm-escape-stay)
language-specific
markdown
(use-package markdown-mode
:commands (markdown-mode gfm-mode)
:mode (("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init (setq markdown-command "multimarkdown"))
org
TODO spellchecking
TODO disable linum on org mode
TODO use org-cliplink
lua
(use-package lua-mode
:commands (lua-mode)
:mode (("\\.lua\\'" . lua-mode)))
nix
(defun nix-flake-current-dir ()
(interactive)
(nix-flake (project-root (project-current))))
(use-package nix-mode
:commands (nix-mode nix-flake)
:bind (("C-c f" . nix-flake-current-dir))
:mode (("\\.nix\\'" . nix-mode)))
lsp
(use-package lsp-mode
:init
(setq lsp-keymap-prefix "C-c s")
:hook
(lsp-mode . lsp-enable-which-key-integration)
:commands
(lsp lsp-deferred))
(with-eval-after-load 'lsp-mode
(add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]venv\\'"))
(use-package lsp-ui
:commands lsp-ui-mode
:after lsp)
;; (use-package lsp-bridge
;; :init (global-lsp-bridge-mode)
;; :custom
;; (lsp-bridge-python-lsp-server "pyright")
;; (lsp-bridge-python-multi-lsp-server "pyright_ruff")
;; )
(setq gc-cons-threshold 100000000)
(setq read-process-output-max (* 1024 1024 3)) ;; 3mb
haskell
(load-library "haskell-mode-autoloads")
;; (use-package intero
;; :config (add-hook 'haskell-mode-hook 'intero-mode)
;; )
TODO intero / haskell mode https://wiki.haskell.org/Emacs
rust
(use-package rust-mode
:commands (rust-mode)
:mode (("\\.rs\\'" . rust-mode)))
terraform-mode
(use-package terraform-mode
:commands (terraform-mode)
:mode (("\\.tf\\'" . terraform-mode)))