dotemacs

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

init.el (17552B)


      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-auto-help 'lazy)
    312  '(completion-auto-select 'second-tab)
    313  '(completion-ignore-case t t)
    314  '(completion-styles '(basic substring initials))
    315  '(completions-header-format nil)
    316  '(context-menu-mode t)
    317  '(create-lockfiles nil)
    318  '(cursor-type 'bar)
    319  '(custom-enabled-themes '(modus-operandi))
    320  '(custom-safe-themes
    321    '("3d94d6d1a1c23113a60c8496c9aed094dbc2695f219e8127bb168d17b1e6dab3" "4b026ac68a1aa4d1a91879b64f54c2490b4ecad8b64de5b1865bca0addd053d9" "58264887d7ab17702ef85bbd96e11bd7f613622ff9c63990be860b958c978f09" "611ef0918b8b413badb8055089b5499c1d4ac20f1861efba8f3bfcb36ad0a448" "15604b083d03519b0c2ed7b32da6d7b2dc2f6630bef62608def60cdcf9216184" "88cb0f9c0c11dbb4c26a628d35eb9239d1cf580cfd28e332e654e7f58b4e721b" "69f7e8101867cfac410e88140f8c51b4433b93680901bb0b52014144366a08c8" "21e3d55141186651571241c2ba3c665979d1e886f53b2e52411e9e96659132d4" default))
    322  '(delete-old-versions t)
    323  '(delete-selection-mode t)
    324  '(denote-modules '(project xref ffap))
    325  '(denote-modules-global-mode t)
    326  '(desktop-save-mode t)
    327  '(dired-dwim-target 'dired-dwim-target-next)
    328  '(dired-kill-when-opening-new-dired-buffer t)
    329  '(dired-mode-hook '(all-the-icons-dired-mode))
    330  '(ediff-split-window-function 'split-window-horizontally)
    331  '(ediff-window-setup-function 'ediff-setup-windows-plain)
    332  '(editorconfig-mode t)
    333  '(elfeed-tube-auto-save-p t)
    334  '(fido-mode t)
    335  '(fido-vertical-mode t)
    336  '(frame-resize-pixelwise t)
    337  '(global-aggressive-indent-mode t)
    338  '(global-auto-revert-mode t)
    339  '(global-diff-hl-mode t)
    340  '(help-window-select t)
    341  '(ibuffer-mode-hook '(all-the-icons-ibuffer-mode))
    342  '(ignored-local-variable-values '((sly-load-failed-fasl . ask)))
    343  '(indent-tabs-mode nil)
    344  '(inferior-lisp-program "sbcl")
    345  '(kept-new-versions 10)
    346  '(kept-old-versions 5)
    347  '(lh/global-resurrect-mode t)
    348  '(marginalia-mode t)
    349  '(mouse-wheel-progressive-speed nil)
    350  '(mouse-wheel-scroll-amount '(5 ((shift) . hscroll) ((meta)) ((control) . text-scale)))
    351  '(notmuch-archive-tags '("-inbox" "-unread"))
    352  '(org-agenda-custom-commands
    353    '(("n" "Agenda and all TODOs"
    354       ((agenda "" nil)
    355        (alltodo "" nil))
    356       nil)
    357      ("g" "GTD View"
    358       ((agenda "" nil)
    359        (tags-todo "+aktion+TODO=\"NEXT\""
    360                   ((org-agenda-overriding-header "Nächste Aktionen:")))
    361        (tags "projekt"
    362              ((org-agenda-overriding-header "Projekte:")
    363               (org-agenda-skip-function 'lh/org-capture-skip-below-toplevel)))
    364        (tags-todo "WAITING"
    365                   ((org-agenda-overriding-header "Warten auf:"))))
    366       nil nil)))
    367  '(org-agenda-loop-over-headlines-in-active-region nil)
    368  '(org-babel-load-languages
    369    '((awk . t)
    370      (lisp . t)
    371      (shell . t)
    372      (emacs-lisp . t)
    373      (restclient . t)))
    374  '(org-html-validation-link "")
    375  '(org-log-done 'time)
    376  '(org-log-done-with-time t)
    377  '(org-refile-use-outline-path 'file)
    378  '(org-src-window-setup 'other-window)
    379  '(org-startup-folded t)
    380  '(org-startup-indented t)
    381  '(org-startup-truncated nil)
    382  '(package-archive-priorities
    383    '(("gnu" . 3)
    384      ("nongnu" . 2)
    385      ("melpa-stable" . 1)
    386      ("melpa" . 0)))
    387  '(package-archives
    388    '(("gnu" . "https://elpa.gnu.org/packages/")
    389      ("nongnu" . "https://elpa.nongnu.org/nongnu/")
    390      ("melpa-stable" . "https://stable.melpa.org/packages/")
    391      ("melpa" . "https://melpa.org/packages/")))
    392  '(package-pinned-packages '((sly . "melpa")))
    393  '(package-selected-packages
    394    '(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 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))
    395  '(pcomplete-ignore-case t t)
    396  '(pixel-scroll-precision-mode t)
    397  '(prism-parens t)
    398  '(read-buffer-completion-ignore-case t)
    399  '(read-file-name-completion-ignore-case t)
    400  '(reb-re-syntax 'string)
    401  '(recentf-max-saved-items 200)
    402  '(recentf-mode t)
    403  '(repeat-mode t)
    404  '(ring-bell-function 'ignore)
    405  '(save-place-mode t)
    406  '(savehist-mode t)
    407  '(scroll-conservatively 100)
    408  '(show-trailing-whitespace t)
    409  '(tab-always-indent 'complete)
    410  '(use-short-answers t)
    411  '(warning-suppress-types '((comp)))
    412  '(which-key-mode t))
    413 (custom-set-faces
    414  ;; custom-set-faces was added by Custom.
    415  ;; If you edit it by hand, you could mess it up, so be careful.
    416  ;; Your init file should contain only one such instance.
    417  ;; If there is more than one, they won't work right.
    418  '(default ((t (:family "Go Mono"))))
    419  '(variable-pitch ((t (:family "IBM Plex Serif")))))
    420 
    421 ;; This is the place where I override all customize stuff
    422 
    423 (with-eval-after-load 'prism
    424   (prism-set-colors
    425     :desaturations '(0)
    426     :lightens '(0)
    427     :colors (modus-themes-with-colors
    428               (list fg-main
    429                     magenta
    430                     cyan-cooler
    431                     magenta-cooler
    432                     blue
    433                     magenta-warmer
    434                     cyan-warmer
    435                     red-cooler
    436                     green
    437                     fg-main
    438                     cyan
    439                     yellow
    440                     blue-warmer
    441                     red-warmer
    442                     green-cooler
    443                     yellow-faint))))
    444 
    445 (let ((feeds (expand-file-name "feeds.el" user-emacs-directory)))
    446   (when (file-exists-p feeds)
    447     (load feeds)))
    448 
    449 (customize-set-value
    450  'elfeed-db-directory
    451  (let ((data-dir (expand-file-name "elfeed" lh/dir-data-home)))
    452    (unless (file-exists-p data-dir)
    453      (make-directory data-dir))
    454    data-dir))
    455 
    456 (customize-set-value
    457  'backup-directory-alist
    458  (let ((backup-dir (concat user-emacs-directory "backup")))
    459    (unless (file-exists-p backup-dir)
    460      (make-directory backup-dir t))
    461    `(("." . ,backup-dir))))
    462 
    463 (let* ((dir (expand-file-name "org" lh/dir-documents))
    464        (roam-dir (expand-file-name "notes" dir)))
    465   (unless (file-exists-p dir)
    466     (make-directory dir t))
    467   (unless (file-exists-p roam-dir)
    468     (make-directory roam-dir t))
    469   (customize-set-value
    470    'org-directory
    471    dir)
    472   (customize-set-value
    473    'org-agenda-files
    474    (list dir))
    475   (customize-set-value
    476    'org-roam-directory
    477    roam-dir)
    478   (with-eval-after-load "org"
    479     (setq org-capture-templates
    480           `(("g" "GTD Inbox" entry
    481              (file ,(expand-file-name "inbox.org" dir))
    482              "* %?")))))
    483 
    484 (customize-set-value
    485  'dired-guess-shell-alist-user
    486  (cond
    487   ((eq system-type 'gnu/linux)
    488    `((,(rx "." (or "png" "jpg" (seq "jp" (? "e") "g"))) "pqiv")
    489      (,(rx "." (or "mkv" "mp4" "webm" "ogv" "avi")) "mpv")
    490      (,(rx "." (or "flac" "ogg" "mp3" "wav")) "mpv --force-window")))
    491   (t nil)))
    492 
    493 (diminish 'which-key-mode)
    494 (diminish 'aggressive-indent-mode)
    495 (diminish 'editorconfig-mode)
    496 
    497 (let ((path (expand-file-name "private.el" user-emacs-directory)))
    498   (when (file-exists-p path)
    499     (load path)))
    500 
    501 (pdf-loader-install)
    502 
    503 (server-start)
    504 (put 'narrow-to-region 'disabled nil)