emacs/config.org
Laurens Miers 377a5cadff fix: whitespace has a separate built-in package
It has a better hook (whitespace-cleanup) than the barebones delete-trailing-whitespace.
2025-05-27 12:51:50 +02:00

1210 lines
33 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+STARTUP: overview
#+TITLE: My Emacs
#+CREATOR: Laurens Miers
#+LANGUAGE: en
* General config
#+BEGIN_SRC emacs-lisp
(use-package emacs
:ensure nil
:custom
(inhibit-startup-message t)
:init
(with-current-buffer (get-buffer-create "*scratch*")
(insert (format ";;
;; ██╗ ██╗███████╗██╗ ██╗ ██████╗
;; ██║ ██║██╔════╝██║ ██║ ██╔═══██╗
;; ███████║█████╗ ██║ ██║ ██║ ██║
;; ██╔══██║██╔══╝ ██║ ██║ ██║ ██║
;; ██║ ██║███████╗███████╗███████╗╚██████╔╝
;; ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚═════╝
;;
;; ███████╗███╗ ███╗ █████╗ ██████╗███████╗
;; ██╔════╝████╗ ████║██╔══██╗██╔════╝██╔════╝
;; █████╗ ██╔████╔██║███████║██║ ███████╗
;; ██╔══╝ ██║╚██╔╝██║██╔══██║██║ ╚════██║
;; ███████╗██║ ╚═╝ ██║██║ ██║╚██████╗███████║
;; ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚══════╝
;;
;; Loading time : %s
;; Packages : %s
;;
"
(emacs-init-time)
(number-to-string (length package-activated-list)))))
(message (emacs-init-time))
)
#+END_SRC
** Package repos
#+BEGIN_SRC emacs-lisp
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
;; Comment/uncomment this line to enable MELPA Stable if desired. See `package-archive-priorities`
;; and `package-pinned-packages`. Most users will not need or want to do this.
;;(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t)
(package-initialize)
#+END_SRC
** Use-package
*** Always ensure
#+BEGIN_SRC emacs-lisp
(require 'use-package-ensure)
(setq use-package-always-ensure t)
#+END_SRC
** Bell
The audible bell is annoying AF.
#+BEGIN_SRC emacs-lisp
(setq visible-bell 1)
#+END_SRC
** Enable column numbers
#+BEGIN_SRC emacs-lisp
(setq column-number-mode 1)
#+END_SRC
** Whitespace cleanup
#+BEGIN_SRC emacs-lisp
(use-package whitespace
:ensure nil
:defer t
:hook (before-save-hook . whitespace-cleanup)
;; if we wanna remove this hook at any time, eval:
;; (remove-hook 'before-save-hook #'whitespace-cleanup)
)
#+END_SRC
** Save history and recent files
#+begin_src emacs-lisp
;; The built-in `savehist-mode' saves minibuffer histories. Vertico
;; can then use that information to put recently selected options at
;; the top.
;;
;; Further reading: https://protesilaos.com/emacs/dotemacs#h:25765797-27a5-431e-8aa4-cc890a6a913a
(savehist-mode 1)
;; The built-in `recentf-mode' keeps track of recently visited files.
;; You can then access those through the `consult-buffer' interface or
;; with `recentf-open'/`recentf-open-files'.
;;
;; I do not use this facility, because the files I care about are
;; either in projects or are bookmarked.
(recentf-mode 1)
#+end_src
** Backups
#+BEGIN_SRC emacs-lisp
(defvar myrmi-backup-directory (concat user-emacs-directory "backups"))
(if (not (file-exists-p myrmi-backup-directory))
(make-directory myrmi-backup-directory t)
)
(setq backup-directory-alist `(("." . ,myrmi-backup-directory)))
(setq make-backup-files t
backup-by-copying t
version-control t
delete-old-versions t
delete-by-moving-to-trash t
kept-old-versions 6
kept-new-versions 9
auto-save-default t
auto-save-timeout 20
auto-save-interval 200
)
#+END_SRC
** Yes-or-no
Because I'm lazy, important yes-or-no questions can be answered with y-or-n:
#+begin_src emacs-lisp
(defalias 'yes-or-no-p 'y-or-n-p)
#+end_src
** Switch windows
#+begin_src emacs-lisp
(global-set-key (kbd "M-o") 'other-window)
#+end_src
** Maximize at startup
More info : https://www.emacswiki.org/emacs/FullScreen
#+begin_src emacs-lisp
(push '(fullscreen . maximized) default-frame-alist)
#+end_src
** ibuffer
Use list-buffers bigger brother.
#+begin_src emacs-lisp
(global-set-key [remap list-buffers] 'ibuffer)
#+end_src
** Mark
#+begin_src emacs-lisp
(global-set-key (kbd "M-SPC") 'mark-word)
#+end_src
** Eldoc
#+BEGIN_SRC emacs-lisp
(use-package eldoc
:ensure nil
:init
(global-eldoc-mode))
#+END_SRC
** Isearch
Inspired by [[https://github.com/LionyxML/emacs-solo][emacs-solo]]:
#+BEGIN_SRC emacs-lisp
(use-package isearch
:ensure nil
:config
(setq isearch-lazy-count t) ; Display number of matches
(setq lazy-count-prefix-format "(%s/%s) ") ; eye-candy to add braces
(defun isearch-copy-selected-word ()
"Copy the current `isearch` selection to the kill ring."
(interactive)
(when isearch-other-end
(let ((selection (buffer-substring-no-properties isearch-other-end (point))))
(kill-new selection)
(isearch-exit))))
;; Bind `M-w` in isearch to copy the selected word, so M-s M-. M-w
;; does a great job of 'copying the current word under cursor'.
(define-key isearch-mode-map (kbd "M-w") 'isearch-copy-selected-word))
#+END_SRC
Reference that might be interesting for later:
https://endlessparentheses.com/leave-the-cursor-at-start-of-match-after-isearch.html
** Flymake
#+BEGIN_SRC emacs-lisp
(use-package flymake
:ensure nil
:defer t
:hook
(prog-mode-hook . flymake-mode)
:custom
(flymake-show-diagnostics-at-end-of-line 'short)
(flymake-indicator-type 'margins)
(flymake-margin-indicators-string
`((error "!" compilation-error)
(warning "?" compilation-warning)
(note "i" compilation-info))
)
)
#+END_SRC
** Abbrev
#+begin_src emacs-lisp
(global-set-key [remap dabbrev-expand] 'hippie-expand)
#+end_src
** Zap
#+begin_src emacs-lisp
(global-set-key (kbd "M-S-z") 'zap-up-to-char)
#+end_src
** Spell checking
Look into customizing the 'ispell' group.
#+begin_src emacs-lisp
(add-hook 'prog-mode-hook 'flyspell-prog-mode)
#+end_src
** Delete selection mode
#+BEGIN_SRC emacs-lisp
(delete-selection-mode t)
#+END_SRC
** Enable disabled commands
Some commands are disabled to protect the user.
Narrow-region/page is a really handy feature, enable it:
#+BEGIN_SRC emacs-lisp
(put 'narrow-to-page 'disabled nil)
(put 'narrow-to-region 'disabled nil)
#+END_SRC
** Adaptive cursor width
Make cursor the width of the character it is under f.e. full width of a tab.
#+BEGIN_SRC emacs-lisp
(setq x-stretch-cursor t)
#+END_SRC
** Enable auto-revert
#+BEGIN_SRC emacs-lisp
(global-auto-revert-mode t)
#+END_SRC
* Resize-mode
Minor-mode to easily resize frames (works with EXWM (firefox, ...)).
Courtesy goes to kuanyui (https://gist.github.com/kuanyui/65a408d393871048771c):
#+BEGIN_SRC emacs-lisp
;;; resize-frame.el --- A minor mode to resize frames easily. -*- lexical-binding: t; -*-
;; Copyright (C) 2014 kuanyui
;; Author: kuanyui <azazabc123@gmail.com>
;; Keywords: frames, tools, convenience
;; License: WTFPL 1.0
;;; Commentary:
;; Press "ESC `" and use arrow-keys or i/j/k/l to adjust frames. press any key to done.
;;; Code:
(defvar resize-frame-map
(let ((map (make-keymap)))
(define-key map (kbd "<up>") 'enlarge-window)
(define-key map (kbd "<down>") 'shrink-window)
(define-key map (kbd "<right>") 'enlarge-window-horizontally)
(define-key map (kbd "<left>") 'shrink-window-horizontally)
(set-char-table-range (nth 1 map) t 'resize-frame-done)
(define-key map (kbd "C-p") 'enlarge-window)
(define-key map (kbd "C-n") 'shrink-window)
(define-key map (kbd "C-f") 'enlarge-window-horizontally)
(define-key map (kbd "C-b") 'shrink-window-horizontally)
map))
(define-minor-mode resize-frame
"A simple minor mode to resize-frame.
C-c C-c to apply."
;; The initial value.
:init-value nil
;; The indicator for the mode line.
:lighter " ResizeFrame"
;; The minor mode bindings.
:keymap resize-frame-map
:global t
(if (<= (length (window-list)) 1)
(progn (setq resize-frame nil)
(message "Only root frame exists, abort."))
(message "Use arrow-keys or C-p/n/f/b to adjust frames.")))
(defun resize-frame-done ()
(interactive)
(setq resize-frame nil)
(message "Done."))
(global-set-key (kbd "C-x C-r") 'resize-frame)
#+END_SRC
* Completion
** Minibuffer
#+BEGIN_SRC emacs-lisp
;; Enable vertico
(use-package vertico
;; :custom
;; (vertico-scroll-margin 0) ;; Different scroll margin
;; (vertico-count 20) ;; Show more candidates
;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer
;; (vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
:init
(vertico-mode))
#+END_SRC
** Consult
#+BEGIN_SRC emacs-lisp
(use-package consult
;; Replace bindings. Lazily loaded by `use-package'.
:bind (;; C-c bindings in `mode-specific-map'
;; ("C-c M-x" . consult-mode-command)
;; ("C-c h" . consult-history)
;; ("C-c k" . consult-kmacro)
;; ("C-c m" . consult-man)
;; ("C-c i" . consult-info)
([remap Info-search] . consult-info)
;; C-x bindings in `ctl-x-map'
("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab
("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
;; Custom M-# bindings for fast register access
;; ("M-#" . consult-register-load)
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
;; ("C-M-#" . consult-register)
;; Other custom bindings
("M-y" . consult-yank-pop) ;; orig. yank-pop
;; M-g bindings in `goto-map'
("M-g e" . consult-compile-error)
("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
("M-g g" . consult-goto-line) ;; orig. goto-line
("M-g M-g" . consult-goto-line) ;; orig. goto-line
;; ("M-g o" . consult-outline) ;; Alternative: consult-org-heading
;; ("M-g m" . consult-mark)
;; ("M-g k" . consult-global-mark)
("M-i" . consult-imenu)
("M-I" . consult-imenu-multi)
;; M-s bindings in `search-map'
("M-s d" . consult-find) ;; Alternative: consult-fd
;; ("M-s c" . consult-locate)
("M-s g" . consult-grep)
;; ("M-s G" . consult-git-grep)
;; ("M-s r" . consult-ripgrep)
("M-s l" . consult-line)
;; ("M-s L" . consult-line-multi)
;; ("M-s k" . consult-keep-lines)
;; ("M-s u" . consult-focus-lines)
;; Isearch integration
("M-s e" . consult-isearch-history)
:map isearch-mode-map
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s l" . consult-line) ;; needed by consult-line to detect isearch
("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
;; Minibuffer history
:map minibuffer-local-map
("M-s" . consult-history) ;; orig. next-matching-history-element
("M-r" . consult-history) ;; orig. previous-matching-history-element
)
;; Enable automatic preview at point in the *Completions* buffer. This is
;; relevant when you use the default completion UI.
:hook (completion-list-mode . consult-preview-at-point-mode)
;; The :init configuration is always executed (Not lazy)
:init
;; Optionally configure the register formatting. This improves the register
;; preview for `consult-register', `consult-register-load',
;; `consult-register-store' and the Emacs built-ins.
;; (setq register-preview-delay 0.5
;; register-preview-function #'consult-register-format)
;; Optionally tweak the register preview window.
;; This adds thin lines, sorting and hides the mode line of the window.
;; (advice-add #'register-preview :override #'consult-register-window)
;; Use Consult to select xref locations with preview
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
;; Configure other variables and modes in the :config section,
;; after lazily loading the package.
;; :config
;; Optionally configure preview. The default value
;; is 'any, such that any key triggers the preview.
;; (setq consult-preview-key 'any)
;; (setq consult-preview-key "M-.")
;; (setq consult-preview-key '("S-<down>" "S-<up>"))
;; For some commands and buffer sources it is useful to configure the
;; :preview-key on a per-command basis using the `consult-customize' macro.
;; (consult-customize
;; consult-theme :preview-key '(:debounce 0.2 any)
;; consult-ripgrep consult-git-grep consult-grep
;; consult-bookmark consult-recent-file consult-xref
;; consult--source-bookmark consult--source-file-register
;; consult--source-recent-file consult--source-project-recent-file
;; :preview-key "M-."
;; :preview-key '(:debounce 0.4 any))
;; Optionally configure the narrowing key.
;; Both < and C-+ work reasonably well.
;; (setq consult-narrow-key "<") ;; "C-+"
;; Optionally make narrowing help available in the minibuffer.
;; You may want to use `embark-prefix-help-command' or which-key instead.
;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
)
#+END_SRC
** In-buffer completion
*** Consult
#+BEGIN_SRC emacs-lisp
(setq completion-in-region-function
(lambda (&rest args)
(apply (if vertico-mode
#'consult-completion-in-region
#'completion--in-region)
args)))
#+END_SRC
*** Corfu
#+BEGIN_SRC
(use-package corfu
;; Optional customizations
:bind (:map corfu-map ("<tab>" . corfu-complete))
:config
(setq tab-always-indent 'complete)
:custom
(corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
(corfu-auto t) ;; Enable auto completion
;; (corfu-separator ?\s) ;; Orderless field separator
;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary
;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match
;; (corfu-preview-current nil) ;; Disable current candidate preview
;; (corfu-preselect 'prompt) ;; Preselect the prompt
;; (corfu-on-exact-match nil) ;; Configure handling of exact matches
;; (corfu-scroll-margin 5) ;; Use scroll margin
;; Enable Corfu only for certain modes. See also `global-corfu-modes'.
;; :hook ((prog-mode . corfu-mode)
;; (shell-mode . corfu-mode)
;; (eshell-mode . corfu-mode))
;; Recommended: Enable Corfu globally. This is recommended since Dabbrev can
;; be used globally (M-/). See also the customization variable
;; `global-corfu-modes' to exclude certain modes.
:init
(global-corfu-mode))
#+end_src
*** Company-mode
#+BEGIN_SRC emacs-lisp
(use-package company
:config
(define-key prog-mode-map
(kbd "TAB")
#'company-indent-or-complete-common)
:init
(global-company-mode)
)
#+END_SRC
** Orderless
#+begin_src emacs-lisp
(use-package orderless
:demand t
:custom
(completion-styles '(orderless basic))
;; (gnus-completion-styles '(orderless substring basic))
;; (completion-category-overrides '((file (styles basic partial-completion))))
;; Below not necessary if using vertico
;; (completion-category-overrides '(
;; (command (styles orderless basic partial-completion))
;; (file (styles orderless basic partial-completion))
;;;; (buffer (styles orderless basic))
;; (variable (styles orderless basic))
;; (symbol (styles orderless basic))
;; (consult-location (styles orderless))
;; (consult-multi (styles orderless))
;; )
;; )
)
#+end_src
** Marginalia
#+begin_src emacs-lisp
;; Enable rich annotations using the Marginalia package
(use-package marginalia
;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding
;; available in the *Completions* buffer, add it to the
;; `completion-list-mode-map'.
:bind (:map minibuffer-local-map
("M-A" . marginalia-cycle))
;; The :init section is always executed.
:init
;; Marginalia must be activated in the :init section of use-package such that
;; the mode gets enabled right away. Note that this forces loading the
;; package.
(marginalia-mode))
#+end_src
* Dired
** Dired-x
#+begin_src emacs-lisp
(with-eval-after-load 'dired
(require 'dired-x)
;; Set dired-x global variables here. For example:
;; (setq dired-x-hands-off-my-keys nil)
)
(add-hook 'dired-mode-hook
(lambda ()
;; Set dired-x buffer-local variables here. For example:
;; (dired-omit-mode 1)
))
#+end_src
** Guess target directory
I currently prefer to have two dired windows open in the same frame.
Instruct dired to 'Prefer next windows on the same frame' when renaming/copying files.
#+BEGIN_SRC emacs-lisp
(setq dired-dwim-target 'dired-dwim-target-next)
#+END_SRC
* Whole-line-or-region
Source:
https://github.com/purcell/whole-line-or-region
Operate on the current line if no region is active.
#+begin_src emacs-lisp
(use-package whole-line-or-region
:config
(whole-line-or-region-global-mode 1)
)
#+end_src
* Terminal
** Eshell
*** Smart mode
Plan 9 smart terminal features, for more info:
https://www.masteringemacs.org/article/complete-guide-mastering-eshell
#+BEGIN_SRC emacs-lisp
(require 'eshell)
(require 'em-smart)
(setq eshell-where-to-jump 'begin)
(setq eshell-review-quick-commands nil)
(setq eshell-smart-space-goes-to-end t)
(add-hook 'eshell-mode-hook 'eshell-smart-initialize)
#+END_SRC
** Toggle between char- and line-mode
Courtesy goes to https://joelmccracken.github.io/entries/switching-between-term-mode-and-line-mode-in-emacs-term/
#+BEGIN_SRC emacs-lisp
(require 'term)
(defun jnm/term-toggle-mode ()
"Toggles term between line mode and char mode"
(interactive)
(if (term-in-line-mode)
(term-char-mode)
(term-line-mode)))
(define-key term-mode-map (kbd "C-c C-j") 'jnm/term-toggle-mode)
(define-key term-mode-map (kbd "C-c C-k") 'jnm/term-toggle-mode)
(define-key term-raw-map (kbd "C-c C-j") 'jnm/term-toggle-mode)
(define-key term-raw-map (kbd "C-c C-k") 'jnm/term-toggle-mode)
#+END_SRC
For the keybindings, we have to defien them in both raw and line mode. From the help page of term mode:
If you define custom keybindings, make sure to assign them to the
correct keymap (or to both): use term-raw-map in raw mode and
term-mode-map in line mode.
* Theme
#+BEGIN_SRC emacs-lisp
(use-package monokai-theme
:init
(load-theme 'monokai t)
)
#+END_SRC
* Dashboard
#+begin_src emacs-lisp
(use-package dashboard
:config
(dashboard-setup-startup-hook))
#+end_src
* Hydra
https://github.com/abo-abo/hydra
#+BEGIN_SRC emacs-lisp
(use-package hydra
:defer t
:config
;; Zoom hydra
(defhydra hydra-zoom (global-map "<f1>")
"zoom"
("g" text-scale-increase "in")
("l" text-scale-decrease "out")
)
)
#+END_SRC
* Zygospore
Revert =C-x 1= by pressing =C-x 1= again:
[[https://github.com/louiskottmann/zygospore.el]]
FYI: At one point, used this together with sr-speedbar. They did not play well together...
#+BEGIN_SRC emacs-lisp
(use-package zygospore
:config
(global-set-key (kbd "C-x 1") 'zygospore-toggle-delete-other-windows)
)
#+END_SRC
* Iedit
Highlight occurences of symbol and replace them simultanously.
Shortkey: =C-;=
https://github.com/victorhge/iedit
#+BEGIN_SRC emacs-lisp
(use-package iedit)
#+END_SRC
* Programming
** Angry faces
#+BEGIN_SRC emacs-lisp
(defface highlight-angry-faces
'(
(default :background "Yellow" :foreground "Red")
)
"Angry faces highlighting."
:group 'basic-faces
)
(mapc (lambda (mode)
(font-lock-add-keywords
mode
'(
("\\<\\(FIXME\\)" 1 'highlight-angry-faces t)
("\\<\\(TODO\\)" 1 'highlight-angry-faces t)
)))
'(text-mode emacs-lisp-mode rust-mode zig-mode c-ts-mode c-mode prog-mode)
)
#+END_SRC
** Electric pair
#+BEGIN_SRC emacs-lisp
(add-hook 'prog-mode-hook 'electric-pair-mode)
#+END_SRC
** Eglot
#+BEGIN_SRC emacs-lisp
(use-package eglot
:ensure nil
:custom
(eglot-autoshutdown t)
:init
(setq eglot-stay-out-of '(xref))
(add-hook 'prog-mode-hook 'eglot-ensure)
(add-hook 'eglot-managed-mode-hook (lambda ()
(if (eglot-managed-p)
(add-hook 'xref-backend-functions 'eglot-xref-backend)
(remove-hook 'xref-backend-functions 'eglot-xref-backend)
)))
)
#+END_SRC
** Markdown-mode
#+BEGIN_SRC emacs-lisp
(use-package markdown-mode)
#+END_SRC
** Yasnippet
#+BEGIN_SRC emacs-lisp
(use-package yasnippet
:hook
(prog-mode . yas-minor-mode)
(org-mode . yas-minor-mode)
(text-mode . yas-minor-mode)
:config
(yas-reload-all)
)
#+END_SRC
** Magit
*** Core
#+BEGIN_SRC emacs-lisp
(use-package magit
:defer t
)
#+END_SRC
** Dumb-jump
#+BEGIN_SRC emacs-lisp
(use-package dumb-jump
:init
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
)
#+END_SRC
** C-programming
*** Tree-sitter
#+BEGIN_SRC emacs-lisp
(add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode))
#+END_SRC
** Compilation
*** Goto end of buffer on completion
Compilation output is almost always bigger than a normal buffer.
Move to the end if the compilation finishes.
#+BEGIN_SRC emacs-lisp
(defun goto-end-compilation-buffer (comp-buffer msg)
(goto-char (point-max))
)
(add-hook 'compilation-finish-functions #'goto-end-compilation-buffer)
#+END_SRC
** Rust
#+BEGIN_SRC emacs-lisp
(use-package rust-mode
:init
(setq rust-mode-treesitter-derive t))
#+END_SRC
** Zig
#+BEGIN_SRC emacs-lisp
(use-package zig-mode)
#+END_SRC
** Python
#+BEGIN_SRC emacs-lisp
(use-package python-mode)
#+END_SRC
* Multiple cursors
#+BEGIN_SRC emacs-lisp
(use-package multiple-cursors
:bind
("C-x r a" . mc/edit-beginnings-of-lines)
("C-x r e" . mc/edit-ends-of-lines)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-c C->" . mc/mark-all-like-this)
)
#+END_SRC
* Volatile highlights
Show/highlight changes when doing undo/yanks/kills/...
https://github.com/k-talo/volatile-highlights.el
#+BEGIN_SRC emacs-lisp
(use-package volatile-highlights
:config
(volatile-highlights-mode t)
)
#+END_SRC
* Comment-dwim-2
Replacement for built-in =comment-dwim=, more comment features.
https://github.com/remyferre/comment-dwim-2
#+BEGIN_SRC emacs-lisp
(use-package comment-dwim-2
:config
(global-set-key (kbd "M-;") 'comment-dwim-2)
)
#+END_SRC
* Projectile
#+BEGIN_SRC emacs-lisp
(use-package projectile
:config
(setq projectile-enable-caching t)
(define-key projectile-mode-map (kbd "C-x p") 'projectile-command-map)
(projectile-mode +1)
(require 'project)
(add-hook 'project-find-functions #'project-projectile)
)
#+END_SRC
* Org
** General config
*** Super/Sub-scripts
Use ={}= for subscripting:
https://orgmode.org/manual/Subscripts-and-superscripts.html
#+BEGIN_SRC emacs-lisp
(setq org-use-sub-superscripts '{})
#+END_SRC
*** Indentation
Preserve indentation in SRC blocks
#+BEGIN_SRC emacs-lisp
(setq org-src-preserve-indentation t)
#+END_SRC
** Org-todo
*** Mark parent entry as DONE when children are DONE
#+BEGIN_SRC emacs-lisp
(defun org-summary-todo (n-done n-not-done)
"Switch entry to DONE when all subentries are done, to TODO otherwise."
(let (org-log-done org-todo-log-states) ; turn off logging
(org-todo (if (= n-not-done 0) "DONE" "TODO"))))
(add-hook 'org-after-todo-statistics-hook #'org-summary-todo)
#+END_SRC
** Org bullets
#+BEGIN_SRC emacs-lisp
(use-package org-bullets
:config
(add-hook 'org-mode-hook (lambda () (org-bullets-mode))))
#+END_SRC
** Org Roam
#+BEGIN_SRC emacs-lisp
(use-package org-roam
:custom
(org-roam-directory "~/projects/notes")
(org-roam-completion-everywhere t)
:config
(org-roam-setup)
(setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags}" 'face 'org-tag)))
(org-roam-db-autosync-mode)
;; Add todo lists to org-agenda
(custom-set-variables '(org-agenda-files (directory-files-recursively org-roam-directory "todo\\.org$")))
(load (expand-file-name "init.el" org-roam-directory) :no-error-if-file-is-missing)
)
#+END_SRC
*** UI
#+BEGIN_SRC emacs-lisp
(use-package org-roam-ui
:ensure
(:host github :repo "org-roam/org-roam-ui" :branch "main" :files ("*.el" "out"))
:after org-roam
;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have
;; a hookable mode anymore, you're advised to pick something yourself
;; if you don't care about startup time, use
;; :hook (after-init . org-roam-ui-mode)
:config
(setq org-roam-ui-sync-theme t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start t)
;; Start UI
;; (org-roam-ui-mode)
)
#+END_SRC
*** Consult
#+BEGIN_SRC emacs-lisp
(use-package consult-org-roam
:ensure t
:after org-roam
:init
(require 'consult-org-roam)
;; Activate the minor mode
(consult-org-roam-mode 1)
;; :custom
;; Use `ripgrep' for searching with `consult-org-roam-search'
;; (consult-org-roam-grep-func #'consult-ripgrep)
;; Configure a custom narrow key for `consult-buffer', default is 'n', this sets it to 'r'
;; (consult-org-roam-buffer-narrow-key ?r)
;; Display org-roam buffers right after non-org-roam buffers
;; in consult-buffer (and not down at the bottom)
;; (consult-org-roam-buffer-after-buffers t)
:config
;; Eventually suppress previewing for certain functions
(consult-customize
consult-org-roam-forward-links
:preview-key "M-.")
:bind
;; Define some convenient keybindings as an addition
("C-c n f" . consult-org-roam-file-find)
("C-c n b" . consult-org-roam-backlinks)
("C-c n B" . consult-org-roam-backlinks-recursive)
("C-c n l" . consult-org-roam-forward-links)
("C-c n s" . consult-org-roam-search)
("C-c n d" . org-roam-dailies-goto-today)
)
#+END_SRC
** Org Download
#+BEGIN_SRC emacs-lisp
(use-package org-download
:config
(add-hook 'dired-mode-hook 'org-download-enable)
)
#+END_SRC
* Elisp
** Add demos to describe-function
#+BEGIN_SRC emacs-lisp
(use-package elisp-demos
:config
(advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1)
)
#+END_SRC
* Custom
** Org-roam
Inspired by https://github.com/org-roam/org-roam/wiki/User-contributed-Tricks#filter-by-a-tag .
#+BEGIN_SRC emacs-lisp
(defun myrmi/org-roam-node-find-tag-filter ()
"Select a single tag from list and filter `org-roam-node' by it."
(interactive)
(let ((tag (car (completing-read-multiple "Tag: "
(org-roam-tag-completions)))))
(org-roam-node-find nil nil
(lambda (node)
(member tag
(org-roam-node-tags node))))))
#+END_SRC
** Font
'Inspired' by https://protesilaos.com/codelog/2024-11-28-basic-emacs-configuration/#h:1e4fde73-a2a2-4dc5-82ad-02cf3884ece6 .
#+BEGIN_SRC emacs-lisp
(let ((mono-spaced-font "Monospace")
(proportionately-spaced-font "Sans"))
(set-face-attribute 'default nil :family mono-spaced-font :height 100)
(set-face-attribute 'fixed-pitch nil :family mono-spaced-font :height 1.0)
(set-face-attribute 'variable-pitch nil :family proportionately-spaced-font :height 1.0))
#+END_SRC
*** Icon fonts
To make this setup work, the user must type M-x and then call the
command 'nerd-icons-install-fonts'. This will store the icon font files
in a local directory (on Linux this is ~/.local/share/fonts).
#+BEGIN_SRC emacs-lisp
(use-package nerd-icons
:ensure t)
(use-package nerd-icons-completion
:ensure t
:after marginalia
:config
(add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))
(use-package nerd-icons-corfu
:ensure t
:after corfu
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(use-package nerd-icons-dired
:ensure t
:hook
(dired-mode . nerd-icons-dired-mode))
#+END_SRC
** Sudo current buffer
#+BEGIN_SRC emacs-lisp
(defun myrmi/sudo-current-buffer ()
"Use TRAMP to `sudo' the current buffer."
(interactive)
(when buffer-file-name
(find-alternate-file
(concat "/sudo:root@localhost:"
buffer-file-name)
)
)
)
#+END_SRC
** Save symbol at point
#+BEGIN_SRC emacs-lisp
(defun myrmi/save-symbol-at-point ()
"Make symbol at point the latest kill in the kill ring."
(interactive)
(let ((symbol (thing-at-point 'symbol)))
(when symbol (kill-new symbol))))
(global-set-key (kbd "C-M-w") 'myrmi/save-symbol-at-point)
#+END_SRC
** Ceedling
#+BEGIN_SRC emacs-lisp
(defvar ceedling-project-file-name "project.yml")
(defvar ceedling-cmd "ceedling")
(defvar ceedling-project-root ".")
(defun myrmi/run-ceedling-tests (&optional file-name)
(interactive)
(let* (
(file-path (or file-name buffer-file-name))
(root-path (or (locate-dominating-file file-path ceedling-project-file-name) ceedling-project-root))
)
(compile
(concat "cd " root-path " && " ceedling-cmd)
)
)
)
#+END_SRC
** Set path to shell path
#+BEGIN_SRC emacs-lisp
(defun set-exec-path-from-shell-PATH ()
(let ((path-from-shell
(replace-regexp-in-string "[[:space:]\n]*$" ""
(shell-command-to-string "$SHELL -l -c 'echo $PATH'"))))
(setenv "PATH" path-from-shell)
(setq exec-path (split-string path-from-shell path-separator))))
(set-exec-path-from-shell-PATH)
#+END_SRC
** Reload dir-locals.el
#+BEGIN_SRC emacs-lisp
(defun myrmi/reload-dir-locals-for-current-buffer ()
"Reload dir locals for the current buffer"
(interactive)
(let ((enable-local-variables :all))
(hack-dir-local-variables-non-file-buffer)))
(defun myrmi/reload-dir-locals-for-all-buffers-in-this-directory ()
"For every buffer with the same `default-directory` as the
current buffer, reload dir-locals."
(interactive)
(let ((dir default-directory))
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (equal default-directory dir)
(myrmi/reload-dir-locals-for-current-buffer))))))
#+END_SRC
** Visit/reload config
These snippets assume my-config-file variable is set outside this configuration.
This should normally be done by the init.el to load this configuration.
#+BEGIN_SRC emacs-lisp
(defun myrmi/visit-config ()
"Visit emacs config"
(interactive)
(find-file my-config-file))
(defun myrmi/reload-config ()
"Reload emacs config at runtime"
(interactive)
(org-babel-load-file my-config-file))
#+END_SRC
** Tips and Tricks
*** Cheat-sheet
| Key | Explanation |
|-----------------------+-----------------------------------------------------------|
| C-h k <key-sequence> | Lookup key sequencesmartparens wrapping |
| C-q <key> | Insert <key> quoted/explicitly. F.e. to insert a tab, ... |
| M-x untabify/tabify | Convert to spaces/tabs |
| M-x describe-bindings | List all mapped keys/commands |
| M-q | Fill paragraph |
** Minibuffer
*** Close minibuffer when pressing C-g
'Inspired' by https://protesilaos.com/codelog/2024-11-28-basic-emacs-configuration/#h:1e4fde73-a2a2-4dc5-82ad-02cf3884ece6 .
#+BEGIN_SRC emacs-lisp
(defun myrmi/keyboard-quit-dwim ()
"Do-What-I-Mean behaviour for a general `keyboard-quit'.
The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open. Whereas we want it to close the
minibuffer, even without explicitly focusing it.
The DWIM behaviour of this command is as follows:
- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
(interactive)
(cond
((region-active-p)
(keyboard-quit))
((derived-mode-p 'completion-list-mode)
(delete-completion-window))
((> (minibuffer-depth) 0)
(abort-recursive-edit))
(t
(keyboard-quit))))
(define-key global-map (kbd "C-g") #'myrmi/keyboard-quit-dwim)
#+END_SRC