dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

init.el (18008B)


      1 ;; -*- lexical-binding: t; -*-
      2 (require 'seq)
      3 (require 'xdg)
      4 
      5 (package-initialize)
      6 
      7 (push
      8  (let ((lisp-path (expand-file-name "lisp" user-emacs-directory)))
      9    (unless (file-exists-p lisp-path)
     10      (make-directory lisp-path))
     11    lisp-path)
     12  load-path)
     13 
     14 (require 'lh)
     15 (require 'lh-insert)
     16 (require 'lh-resurrect)
     17 (require 'iso-transl)
     18 
     19 (with-eval-after-load 'paredit
     20   (require 'paredit-menu))
     21 (with-eval-after-load 'restclient
     22   (require 'restclient-capf))
     23 (with-eval-after-load 'elfeed
     24   (require 'elfeed-tube)
     25   (require 'elfeed-tube-mpv)
     26   (elfeed-tube-setup))
     27 
     28 (setq org-roam-v2-ack t)
     29 
     30 (setq lh/dir-documents
     31       (expand-file-name
     32        (cond ((eq system-type 'gnu/linux) (or (xdg-user-dir "DOCUMENTS") "~/Documents"))
     33              ((eq system-type 'windows-nt) "~/Documents"))))
     34 (setq lh/dir-data-home
     35       (expand-file-name
     36        (cond ((eq system-type 'gnu/linux) (or (xdg-data-home) "~/.local/share"))
     37              ((eq system-type 'windows-nt) (getenv "APPDATA")))))
     38 
     39 (defadvice split-window-below (after lh/split-window-below activate)
     40   (other-window 1))
     41 (defadvice split-window-right (after lh/split-window-right activate)
     42   (other-window 1))
     43 (defadvice toggle-frame-fullscreen (before lh/toggle-frame-fullscreen-bars activate)
     44   (menu-bar-mode (if menu-bar-mode -1 1))
     45   (tool-bar-mode (if tool-bar-mode -1 1))
     46   (scroll-bar-mode (if scroll-bar-mode -1 1)))
     47 
     48 (defun lh/org-capture-skip-below-toplevel ()
     49   (when (> (org-current-level) 1)
     50     (save-excursion (org-end-of-subtree t))))
     51 
     52 (defun lh/diff-file-changes ()
     53   (interactive)
     54   (let ((file-name (make-temp-file "emacs-diff-"))
     55         (original-file-name (buffer-file-name)))
     56     (unwind-protect
     57         (progn
     58           (unwind-protect
     59               (progn
     60                 (set-visited-file-name file-name)
     61                 (save-buffer))
     62             (set-visited-file-name original-file-name))
     63           (diff original-file-name file-name nil t))
     64       (delete-file file-name))))
     65 
     66 (defun lh/indent-region-inhibit-message (start end &optional column)
     67   (interactive "r\nP")
     68   (let ((inhibit-message t))
     69     (indent-region start end column)))
     70 
     71 (defmacro lh/global-set-keys (keys-alist)
     72   `(progn
     73      ,@(seq-map
     74         (lambda (x)
     75           `(global-set-key (kbd ,(car x)) #',(cdr x)))
     76         keys-alist)))
     77 
     78 (defmacro lh/define-keys (keymap keys-alist &optional after)
     79   (let ((defines (seq-map
     80                   (lambda (x)
     81                     `(define-key
     82                       ,keymap
     83                       ,(let ((key (car x)))
     84                          (cond
     85                           ((stringp key) `(kbd ,key))
     86                           (t key)))
     87                       #',(cdr x)))
     88                   keys-alist)))
     89     (if (null after)
     90         (cons 'progn defines)
     91       `(with-eval-after-load ',after
     92          ,@defines))))
     93 
     94 (lh/global-set-keys
     95  (("C-x C-M-t" . transpose-regions)
     96   ("C-x K" . kill-this-buffer)
     97   ("C-x C-b" . ibuffer)
     98 
     99   ;;;; Consult bindings
    100   ;; C-c bindings (mode-specific-map)
    101   ("C-c h" . consult-history)
    102   ("C-c m" . consult-mode-command)
    103   ("C-c b" . consult-bookmark)
    104   ("C-c k" . consult-kmacro)
    105   ;; C-x bindings (ctl-x-map)
    106   ("C-x M-:" . consult-complex-command)     ;; orig. repeat-complex-command
    107   ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
    108   ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
    109   ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
    110   ;; Custom M-# bindings for fast register access
    111   ("M-#" . consult-register-load)
    112   ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
    113   ("C-M-#" . consult-register)
    114   ;; Other custom bindings'
    115   ("<help> a" . consult-apropos)            ;; orig. apropos-command
    116   ;; M-g bindings (goto-map)
    117   ("M-g e" . consult-compile-error)
    118   ("M-g f" . consult-flymake)               ;; Alternative: consult-flycheck
    119   ("M-g g" . consult-goto-line)             ;; orig. goto-line
    120   ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
    121   ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
    122   ("M-g m" . consult-mark)
    123   ("M-g k" . consult-global-mark)
    124   ("M-g i" . consult-imenu)
    125   ("M-g I" . consult-imenu-multi)
    126   ;; M-s bindings (search-map)
    127   ("M-s f" . consult-find)
    128   ("M-s L" . consult-locate)
    129   ("M-s g" . consult-grep)
    130   ("M-s G" . consult-git-grep)
    131   ("M-s l" . consult-line)
    132   ("M-s m" . consult-multi-occur)
    133   ("M-s k" . consult-keep-lines)
    134   ("M-s u" . consult-focus-lines)
    135   ("M-s s" . consult-eglot-symbols)
    136   ;; Isearch integration
    137   ("M-s e" . consult-isearch)
    138 
    139   ("C-." . embark-act)
    140   ("M-." . embark-dwim)
    141 
    142   ("C-+" . er/expand-region)
    143 
    144   ("M-o" . ace-window)
    145 
    146   ("C-c '" . imenu-list-smart-toggle)
    147 
    148   ("C-c r l" . org-roam-buffer-toggle)
    149   ("C-c r f" . org-roam-node-find)
    150   ("C-c r i" . org-roam-node-insert)
    151 
    152   ("C-h f" . helpful-callable)
    153   ("C-h F" . helpful-function)
    154   ("C-h C" . helpful-command)
    155   ("C-h v" . helpful-variable)
    156   ("C-h k" . helpful-key)
    157   ("C-c C-d" . helpful-at-point)
    158 
    159   ("C-S-p" . lh/move-line-up)
    160   ("M-<up>" . lh/move-line-up)
    161   ("C-S-n" . lh/move-line-down)
    162   ("M-<down>" . lh/move-line-down)
    163 
    164   ("M-s r" . deadgrep)
    165 
    166   ("C-c p" . lh/pop-out-buffer)
    167   ("C-c n" . lh/buffer-create-new)
    168   ("C-c a" . org-agenda)
    169   ("C-c c" . org-capture)
    170   ("C-c l" . org-store-link)
    171 
    172   ("C-c i j" . lh/insert-json-encoded)
    173   ("C-c i i s" . lh/insert-random-sha1)
    174   ("C-c i i u" . uuidgen)
    175   ("C-c i r f" . lh/insert-number-from-register-format)
    176 
    177   ("C-c e x" . lh/xml-escape-region)
    178 
    179   ("<mouse-8>" . xref-go-back)
    180   ("<mouse-9>" . xref-go-forward)))
    181 
    182 (lh/define-keys icomplete-fido-mode-map
    183                 (("TAB" . icomplete-force-complete)
    184                  ("<left>" . left-char)
    185                  ("<right>" . right-char)
    186                  ("^" . icomplete-fido-backward-updir)
    187                  ("DEL" . backward-delete-char))
    188                 icomplete)
    189 (lh/define-keys isearch-mode-map
    190                 (("M-e" . consult-isearch)
    191                  ("M-s e" . consult-isearch)
    192                  ("M-s l" . consult-line)))
    193 (lh/define-keys paredit-mode-map
    194                 (("M-s" . nil)
    195                  ("M-S" . paredit-splice-sexp))
    196                 paredit)
    197 (lh/define-keys elfeed-search-mode-map
    198                 (("G" . elfeed-update))
    199                 elfeed)
    200 (lh/define-keys json-mode-map
    201                 (("C-c C-j" . jq-interactively))
    202                 json-mode)
    203 (lh/define-keys sly-inspector-mode-map
    204                 (("<mouse-8>" . sly-inspector-pop)
    205                  ("<mouse-9>" . sly-inspector-next))
    206                 sly)
    207 
    208 (with-eval-after-load 'icomplete
    209   (define-key icomplete-minibuffer-map (kbd "C-.") nil))
    210 
    211 (defun lh/elfeed-show-visit ()
    212   (interactive)
    213   (let ((link (elfeed-entry-link elfeed-show-entry)))
    214     (if (string-match-p (rx
    215                          line-start
    216                          "http" (opt "s") "://"
    217                          (opt "www.")
    218                          "youtube.com/")
    219                         link)
    220         (elfeed-tube-mpv (point))
    221       (elfeed-show-visit))))
    222 
    223 (lh/define-keys elfeed-show-mode-map
    224                 (("b" . lh/elfeed-show-visit)
    225                  ("F" . elfeed-tube-fetch)
    226                  ([remap save-buffer] . elfeed-tube-save)
    227                  ("C-c C-f" . elfeed-tube-mpv-follow-mode)
    228                  ("C-c C-w" . elfeed-tube-mpv-where))
    229                 elfeed)
    230 (lh/define-keys elfeed-search-mode-map
    231                 (("F" . elfeed-tube-fetch)
    232                  ([remap save-buffer] . elfeed-tube-save))
    233                 elfeed)
    234 
    235 (defun corfu-insert-with-return ()
    236   (interactive)
    237   (let ((idx corfu--index))
    238     (corfu-insert)
    239     (when (< idx 0)
    240       (funcall (key-binding (kbd "RET")))
    241       (indent-according-to-mode))))
    242 
    243 (lh/define-keys corfu-map
    244                 (("RET" . corfu-insert-with-return))
    245                 corfu)
    246 
    247 (defun lh/lisp-mode-hook ()
    248   (paredit-mode 1)
    249   (prism-mode 1)
    250   (highlight-function-calls-mode 1))
    251 
    252 (add-hook 'lisp-mode-hook 'lh/lisp-mode-hook)
    253 (add-hook 'emacs-lisp-mode-hook 'lh/lisp-mode-hook)
    254 ;; Aggressive Intent mode causes 100% CPU for me whenever the sly repl prints warnings
    255 (add-hook 'sly-mrepl-mode-hook
    256           (lambda ()
    257             (aggressive-indent-mode -1)))
    258 (add-hook 'embark-collect-mode-hook 'consult-preview-at-point-mode)
    259 (add-hook 'prog-mode-hook
    260           (lambda ()
    261             (setq-local show-trailing-whitespace t)))
    262 
    263 ;; notmuch's tag selection doesnt work with consult-completing-read-multiple
    264 ;; items need to be trimmed
    265 (defun lh/notmuch-read-tag-changes-trim (tags)
    266   (seq-map (lambda (x)
    267              (string-trim-right x))
    268            tags))
    269 
    270 (advice-add #'notmuch-read-tag-changes :filter-return #'lh/notmuch-read-tag-changes-trim)
    271 
    272 ;; Fixes bug in minibuffer.el that causes consult to not work without any text
    273 ;; https://github.com/minad/consult/issues/566
    274 ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=55205
    275 (defun lh/advice-fix-minibuffer-consult (func &rest args)
    276   (apply func (list (car args)
    277                     (cadr args)
    278                     (copy-sequence (caddr args)))))
    279 
    280 (advice-add #'completion--replace :around #'lh/advice-fix-minibuffer-consult)
    281 
    282 ;; file-name-all-completions seems to return file names in whatever order the
    283 ;; filesystem feels like returning. This is super annoying with the flex
    284 ;; completion style. If the FS returns "barfoo" before "foobar", flex completion
    285 ;; will match "barfoo" first. This advice sorts the file name list.
    286 (defun lh/advice-sort-file-name-all-completions (files)
    287   (sort files #'string<))
    288 
    289 (advice-add 'file-name-all-completions :filter-return 'lh/advice-sort-file-name-all-completions)
    290 
    291 (setq completion-ignore-case t)
    292 
    293 (custom-set-variables
    294  ;; custom-set-variables was added by Custom.
    295  ;; If you edit it by hand, you could mess it up, so be careful.
    296  ;; Your init file should contain only one such instance.
    297  ;; If there is more than one, they won't work right.
    298  '(aggressive-indent-region-function 'lh/indent-region-inhibit-message)
    299  '(all-the-icons-completion-mode t)
    300  '(auth-sources '(default))
    301  '(aw-dispatch-always t)
    302  '(aw-keys '(97 115 100 102 103 104 106 107 108))
    303  '(aw-scope 'frame)
    304  '(backup-by-copying t)
    305  '(before-save-hook '(whitespace-cleanup))
    306  '(bookmark-completion-ignore-case nil)
    307  '(calendar-christian-all-holidays-flag nil)
    308  '(calendar-mark-holidays-flag t)
    309  '(calendar-week-start-day 1)
    310  '(column-number-mode t)
    311  '(completion-ignore-case t t)
    312  '(completion-styles '(basic substring initials))
    313  '(context-menu-mode t)
    314  '(corfu-auto t)
    315  '(corfu-auto-delay 0.0)
    316  '(corfu-auto-prefix 1)
    317  '(corfu-popupinfo-delay '(0 . 0))
    318  '(corfu-popupinfo-hide nil)
    319  '(corfu-popupinfo-mode t)
    320  '(corfu-preselect 'prompt)
    321  '(corfu-quit-at-boundary t)
    322  '(create-lockfiles nil)
    323  '(cursor-type 'bar)
    324  '(custom-enabled-themes '(modus-operandi))
    325  '(custom-safe-themes
    326    '("1ea82e39d89b526e2266786886d1f0d3a3fa36c87480fad59d8fab3b03ef576e" "3d94d6d1a1c23113a60c8496c9aed094dbc2695f219e8127bb168d17b1e6dab3" "4b026ac68a1aa4d1a91879b64f54c2490b4ecad8b64de5b1865bca0addd053d9" "58264887d7ab17702ef85bbd96e11bd7f613622ff9c63990be860b958c978f09" "611ef0918b8b413badb8055089b5499c1d4ac20f1861efba8f3bfcb36ad0a448" "15604b083d03519b0c2ed7b32da6d7b2dc2f6630bef62608def60cdcf9216184" "88cb0f9c0c11dbb4c26a628d35eb9239d1cf580cfd28e332e654e7f58b4e721b" "69f7e8101867cfac410e88140f8c51b4433b93680901bb0b52014144366a08c8" "21e3d55141186651571241c2ba3c665979d1e886f53b2e52411e9e96659132d4" default))
    327  '(delete-old-versions t)
    328  '(delete-selection-mode t)
    329  '(denote-modules '(project xref ffap))
    330  '(denote-modules-global-mode t)
    331  '(desktop-save-mode t)
    332  '(dired-dwim-target 'dired-dwim-target-next)
    333  '(dired-kill-when-opening-new-dired-buffer t)
    334  '(dired-mode-hook '(all-the-icons-dired-mode))
    335  '(display-buffer-alist
    336    '(("\\\\*sly-db for"
    337       (display-buffer-reuse-window display-buffer-below-selected)
    338       (dedicated . t)
    339       (window-height . 0.3))
    340      ("\\\\*Async Shell Command\\\\*" display-buffer-no-window)))
    341  '(ediff-split-window-function 'split-window-horizontally)
    342  '(ediff-window-setup-function 'ediff-setup-windows-plain)
    343  '(editorconfig-mode t)
    344  '(elfeed-tube-auto-save-p t)
    345  '(fido-mode t)
    346  '(fido-vertical-mode t)
    347  '(focus-follows-mouse t)
    348  '(frame-resize-pixelwise t)
    349  '(global-aggressive-indent-mode t)
    350  '(global-auto-revert-mode t)
    351  '(global-corfu-mode t)
    352  '(global-diff-hl-mode t)
    353  '(help-window-select t)
    354  '(ibuffer-mode-hook '(all-the-icons-ibuffer-mode))
    355  '(ignored-local-variable-values '((sly-load-failed-fasl . ask)))
    356  '(indent-tabs-mode nil)
    357  '(inferior-lisp-program "sbcl")
    358  '(kept-new-versions 10)
    359  '(kept-old-versions 5)
    360  '(lh/global-resurrect-mode t)
    361  '(marginalia-mode t)
    362  '(mouse-autoselect-window 0.2)
    363  '(mouse-wheel-progressive-speed nil)
    364  '(mouse-wheel-scroll-amount '(5 ((shift) . hscroll) ((meta)) ((control) . text-scale)))
    365  '(notmuch-archive-tags '("-inbox" "-unread"))
    366  '(org-agenda-custom-commands
    367    '(("n" "Agenda and all TODOs"
    368       ((agenda "" nil)
    369        (alltodo "" nil))
    370       nil)
    371      ("g" "GTD View"
    372       ((agenda "" nil)
    373        (tags-todo "+aktion+TODO=\"NEXT\""
    374                   ((org-agenda-overriding-header "Nächste Aktionen:")))
    375        (tags "projekt"
    376              ((org-agenda-overriding-header "Projekte:")
    377               (org-agenda-skip-function 'lh/org-capture-skip-below-toplevel)))
    378        (tags-todo "WAITING"
    379                   ((org-agenda-overriding-header "Warten auf:"))))
    380       nil nil)))
    381  '(org-agenda-loop-over-headlines-in-active-region nil)
    382  '(org-babel-load-languages '((awk . t) (lisp . t) (shell . t) (emacs-lisp . t)))
    383  '(org-html-validation-link "")
    384  '(org-log-done 'time)
    385  '(org-log-done-with-time t)
    386  '(org-refile-use-outline-path 'file)
    387  '(org-src-window-setup 'other-window)
    388  '(org-startup-folded t)
    389  '(org-startup-indented t)
    390  '(org-startup-truncated nil)
    391  '(package-archive-priorities
    392    '(("gnu" . 3)
    393      ("nongnu" . 2)
    394      ("melpa-stable" . 1)
    395      ("melpa" . 0)))
    396  '(package-archives
    397    '(("gnu" . "https://elpa.gnu.org/packages/")
    398      ("nongnu" . "https://elpa.nongnu.org/nongnu/")
    399      ("melpa-stable" . "https://stable.melpa.org/packages/")
    400      ("melpa" . "https://melpa.org/packages/")))
    401  '(package-pinned-packages '((sly . "melpa")))
    402  '(package-selected-packages
    403    '(highlight-function-calls prism modus-themes imenu-list diff-hl embark-consult embark all-the-icons-completion all-the-icons-ibuffer all-the-icons-dired sly-named-readtables sly-macrostep denote-refs denote-menu denote ox-epub ob-powershell powershell web-mode lexic editorconfig elfeed-tube-mpv elfeed-tube restclient-jq graphviz-dot-mode consult-eglot jq-mode ob-restclient restclient deadgrep helpful pdf-tools paredit-menu paredit corfu sly eglot aggressive-indent project nov nhexl-mode elfeed magit yaml-mode json-mode lua-mode go-mode org-contrib org ace-window expand-region consult marginalia uuidgen diminish which-key))
    404  '(pcomplete-ignore-case t t)
    405  '(pixel-scroll-precision-mode t)
    406  '(prism-parens t)
    407  '(read-buffer-completion-ignore-case t)
    408  '(read-file-name-completion-ignore-case t)
    409  '(reb-re-syntax 'string)
    410  '(recentf-max-saved-items 200)
    411  '(recentf-mode t)
    412  '(repeat-mode t)
    413  '(ring-bell-function 'ignore)
    414  '(save-place-mode t)
    415  '(savehist-mode t)
    416  '(scroll-conservatively 100)
    417  '(show-trailing-whitespace t)
    418  '(tab-always-indent 'complete)
    419  '(use-short-answers t)
    420  '(warning-suppress-types '((comp)))
    421  '(which-key-mode t))
    422 (custom-set-faces
    423  ;; custom-set-faces was added by Custom.
    424  ;; If you edit it by hand, you could mess it up, so be careful.
    425  ;; Your init file should contain only one such instance.
    426  ;; If there is more than one, they won't work right.
    427  '(default ((t (:family "Go Mono"))))
    428  '(variable-pitch ((t (:family "IBM Plex Serif")))))
    429 
    430 ;; This is the place where I override all customize stuff
    431 
    432 (with-eval-after-load 'prism
    433   (prism-set-colors
    434     :desaturations '(0)
    435     :lightens '(0)
    436     :colors (modus-themes-with-colors
    437               (list fg-main
    438                     magenta
    439                     cyan-cooler
    440                     magenta-cooler
    441                     blue
    442                     magenta-warmer
    443                     cyan-warmer
    444                     red-cooler
    445                     green
    446                     fg-main
    447                     cyan
    448                     yellow
    449                     blue-warmer
    450                     red-warmer
    451                     green-cooler
    452                     yellow-faint))))
    453 
    454 (let ((feeds (expand-file-name "feeds.el" user-emacs-directory)))
    455   (when (file-exists-p feeds)
    456     (load feeds)))
    457 
    458 (customize-set-value
    459  'elfeed-db-directory
    460  (let ((data-dir (expand-file-name "elfeed" lh/dir-data-home)))
    461    (unless (file-exists-p data-dir)
    462      (make-directory data-dir))
    463    data-dir))
    464 
    465 (customize-set-value
    466  'backup-directory-alist
    467  (let ((backup-dir (concat user-emacs-directory "backup")))
    468    (unless (file-exists-p backup-dir)
    469      (make-directory backup-dir t))
    470    `(("." . ,backup-dir))))
    471 
    472 (let* ((dir (expand-file-name "org" lh/dir-documents))
    473        (roam-dir (expand-file-name "notes" dir)))
    474   (unless (file-exists-p dir)
    475     (make-directory dir t))
    476   (unless (file-exists-p roam-dir)
    477     (make-directory roam-dir t))
    478   (customize-set-value
    479    'org-directory
    480    dir)
    481   (customize-set-value
    482    'org-agenda-files
    483    (list dir))
    484   (customize-set-value
    485    'org-roam-directory
    486    roam-dir)
    487   (with-eval-after-load "org"
    488     (setq org-capture-templates
    489           `(("g" "GTD Inbox" entry
    490              (file ,(expand-file-name "inbox.org" dir))
    491              "* %?")))))
    492 
    493 (customize-set-value
    494  'dired-guess-shell-alist-user
    495  (cond
    496   ((eq system-type 'gnu/linux)
    497    `((,(rx "." (or "png" "jpg" (seq "jp" (? "e") "g"))) "pqiv")
    498      (,(rx "." (or "mkv" "mp4" "webm" "ogv" "avi")) "mpv")
    499      (,(rx "." (or "flac" "ogg" "mp3" "wav")) "mpv --force-window")))
    500   (t nil)))
    501 
    502 (diminish 'which-key-mode)
    503 (diminish 'aggressive-indent-mode)
    504 (diminish 'editorconfig-mode)
    505 
    506 (let ((path (expand-file-name "private.el" user-emacs-directory)))
    507   (when (file-exists-p path)
    508     (load path)))
    509 
    510 (pdf-loader-install)
    511 
    512 (server-start)
    513 (put 'narrow-to-region 'disabled nil)