#+TITLE: GNU Emacs Configuration
#+AUTHOR: Terje Larsen

* Overview
Terje's GNU Emacs configuration.

This configuration is making a lot of assumptions as it is built as a package with [[https://nix.dev][Nix]]. This means that all
packages are expected to be on the =load-path= already and compatibility is only guaranteed with the GNU Emacs
version that is part of this package. Currently GNU Emacs 31.0.50.

The rough details:
- themes :: [[file:lisp/readable-typo-theme.el][readable-typo-theme]] and [[file:lisp/readable-mono-theme.el][readable-mono-theme]]
  - fonts :: [[https://github.com/be5invis/Iosevka][Iosevka Curly Slab, Iosevka Aile, Iosevka Etoile]]
- buffer completion :: [[https://github.com/minad/corfu][corfu]], =completion-preview=
- minibuffer completion :: [[https://github.com/minad/vertico][vertico]]
- package configuration :: [[info:use-package#Top][use-package]]
- key definitions :: =keymap=, =repeat=
- code navigation :: [[info:emacs#Xref][xref]], [[https://github.com/jacktasia/dumb-jump][dumb-jump]], [[https://github.com/vspinu/imenu-anywhere][imenu-anywhere]], [[info:emacs#Visiting][find-sibling-file]]
- syntax checker :: [[info:flymake#Top][flymake]]
- persistence :: =desktop=
- language server integration :: [[info:eglot#Top][eglot]]
- structured editing :: [[https://github.com/AmaiKinono/puni][puni]]

Nix:
- Package management via [[https://github.com/emacs-twist/twist.nix][twist.nix]]
- GNU Emacs via [[https://github.com/nix-community/emacs-overlay][emacs-overlay]]

** Screenshots
*** org-mode
#+CAPTION: org-mode light
[[./media/org-mode-light.png]]

#+CAPTION: org-mode dark
[[./media/org-mode-dark.png]]

*** markdown-mode
#+CAPTION: markdown-mode light
[[./media/markdown-mode-light.png]]

#+CAPTION: markdown-mode dark
[[./media/markdown-mode-dark.png]]

*** emacs-lisp-mode
#+CAPTION: emacs-lisp-mode light
[[./media/emacs-lisp-mode-light.png]]

#+CAPTION: emacs-lisp-mode dark
[[./media/emacs-lisp-mode-dark.png]]

* Early initialization
#+begin_src emacs-lisp :tangle early-init.el
;;; early-init.el --- Early Initialization -*- lexical-binding: t; -*-
#+end_src

** Debug
Provide an easy way to toggle debug mode which will set certain variables to produce more
informative output. It can be set either by providing the environment variable =DEBUG= or
start Emacs with =--debug-init=.
#+begin_src emacs-lisp :tangle early-init.el
(when (getenv "DEBUG") (setq init-file-debug t))
(setq debug-on-error (and (not noninteractive) init-file-debug))
#+end_src

** Startup
Time the startup and display the startup time after completed.
#+begin_src emacs-lisp :tangle early-init.el
(add-hook 'emacs-startup-hook
          (lambda ()
            (message "Loaded Emacs in %.03fs"
                     (float-time (time-subtract after-init-time before-init-time)))))
#+end_src

Temporarily reduce garbage collection to gain some performance boost during startup.
#+begin_src emacs-lisp :tangle early-init.el
(let ((normal-gc-cons-threshold gc-cons-threshold)
      (normal-gc-cons-percentage gc-cons-percentage)
      (normal-file-name-handler-alist file-name-handler-alist)
      (init-gc-cons-threshold most-positive-fixnum)
      (init-gc-cons-percentage 0.6))
  (setq gc-cons-threshold init-gc-cons-threshold
        gc-cons-percentage init-gc-cons-percentage
        file-name-handler-alist nil)
  (add-hook 'after-init-hook
            (lambda ()
              (setq gc-cons-threshold normal-gc-cons-threshold
                    gc-cons-percentage normal-gc-cons-percentage
                    file-name-handler-alist normal-file-name-handler-alist))))
#+end_src

Inhibit startup screen and messages. If you are new to Emacs it is recommended to not
disable the startup screen as it has great content to get you going.
#+begin_src emacs-lisp :tangle early-init.el
(setq inhibit-startup-echo-area-message t)
(setq inhibit-startup-screen t)
(setq initial-scratch-message nil)
#+end_src

Performance tweaks.  Don't load default library and use =fundamental-mode= to reduce amount
of hooks.
#+begin_src emacs-lisp :tangle early-init.el
(setq inhibit-default-init t)
(setq initial-major-mode 'fundamental-mode)
#+end_src

Disable =package.el= support. Packages are managed via Nix and disabling this gives a slight
performance boost.
#+begin_src emacs-lisp :tangle early-init.el
(setq package-enable-at-startup nil)
#+end_src

** UI
Disable GUI components.
#+begin_src emacs-lisp :tangle early-init.el
(setq use-dialog-box nil)
(push '(undecorated . t) default-frame-alist)
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)
#+end_src

Don't implicitly resize frames when changing various settings.
#+begin_src emacs-lisp :tangle early-init.el
(setq frame-inhibit-implied-resize t)
(defvar global-text-scale-adjust-resizes-frames t)
#+end_src

Allow resizing by dragging internal border.
#+begin_src emacs-lisp :tangle early-init.el
(push '(drag-internal-border . t) default-frame-alist)
#+end_src

Ignore X resources.
#+begin_src emacs-lisp :tangle early-init.el
(advice-add #'x-apply-session-resources :override #'ignore)
#+end_src

* Initialization
#+begin_src emacs-lisp :tangle yes
;;; init.el --- Initialization -*- lexical-binding: t; -*-
#+end_src

** Package configuration
*** use-package
A macro for package customization, see  [[info:use-package#Top][use-package]] for further details.

This configuration is intended to support byte-compiling and is compiling with the strict option
~byte-compile-error-on-warn t~. See the section about [[info:use-package#Byte-compiling][byte-compiling]] with information on how to support this use case.
#+begin_src emacs-lisp :tangle yes
(eval-when-compile
  (require 'use-package))

(eval-and-compile
  (defun use-package-ensure-ignore (&rest _args) t)
  (setq use-package-ensure-function #'use-package-ensure-ignore)

  (setq use-package-always-defer t)
  (setq use-package-hook-name-suffix nil))

(if init-file-debug
    (setq use-package-verbose t
          use-package-expand-minimally nil
          use-package-compute-statistics t)
  (setq use-package-verbose nil
        use-package-expand-minimally t))
#+end_src

** Data
Provide a location where Emacs can store data and cache.
#+begin_src emacs-lisp :tangle yes
(defvar data-dir
  (if (getenv "XDG_DATA_HOME")
      (concat (getenv "XDG_DATA_HOME") "/emacs/")
    (expand-file-name "~/.local/share/emacs/"))
  "Directory for data.")

(defvar cache-dir
  (if (getenv "XDG_CACHE_HOME")
      (concat (getenv "XDG_CACHE_HOME") "/emacs/")
    (expand-file-name "~/.cache/emacs/"))
  "Directory for cache.")

(defvar pictures-dir
  (or (getenv "XDG_PICTURES_DIR")
      (expand-file-name "~/Pictures/"))
  "Directory for pictures.")
#+end_src

*** Customization
Put custom definitions in a temporary file so it doesn't grow over time. This means all
customization will disappear upon reboot and instead needs to be managed via the
initialization file.
#+begin_src emacs-lisp :tangle yes
(setq custom-file (expand-file-name "custom.el" temporary-file-directory))
#+end_src

*** no-littering
Help keeping ~user-emacs-directory~ clean.
#+begin_src emacs-lisp :tangle yes
(use-package no-littering
  :ensure t
  :demand t
  :init
  (setq no-littering-etc-directory data-dir)
  (setq no-littering-var-directory cache-dir))
#+end_src

** Performance
No second pass of case-insensitive search over =auto-mode-alist=.
#+begin_src emacs-lisp :tangle yes
(setq auto-mode-case-fold nil)
#+end_src

Give up some bidirectional functionality for slightly faster re-display.
#+begin_src emacs-lisp :tangle yes
(setq bidi-inhibit-bpa t)
(setq-default bidi-display-reordering 'left-to-right
              bidi-paragraph-direction 'left-to-right)
#+end_src

Defer processing font faces while scrolling and typing.
#+begin_src emacs-lisp :tangle yes
(setq fast-but-imprecise-scrolling t)
(setq jit-lock-defer-time 0)
#+end_src

*** gcmh
Adjust Garbage Collector threshold depending on activity and try to run garbage collection
during idle instead.
#+begin_src emacs-lisp :tangle yes
(use-package gcmh
  :ensure t
  :hook
  (after-init-hook . gcmh-mode)
  :init
  (setq gcmh-idle-delay 5)
  (setq gcmh-high-cons-threshold (* 16 1024 1024)) ; 16MB
  (setq gcmh-verbose init-file-debug))
#+end_src

*** explain-pause-mode
Track how much time is spent in each function and present a view to investigate this data.
#+begin_src emacs-lisp :tangle no
(use-package explain-pause-mode
  :ensure t
  :commands
  (explain-pause-mode)
  :init
  (when init-file-debug
    (explain-pause-mode 1)))
#+end_src

*** vlf
Large file support. This can view/edit/search and compare large files.
#+begin_src emacs-lisp :tangle no
(use-package vlf :ensure t)
#+end_src

** Libraries
*** all-the-icons
Library for displaying icons. I am trying to use icons where possible.
#+begin_src emacs-lisp :tangle yes
(use-package all-the-icons
  :ensure t
  :commands
  (all-the-icons-faicon all-the-icons-octicon))
#+end_src

*** cl-lib
Library providing functions inspired by Common Lisp. In many cases these functions feel
more natural to use.
#+begin_src emacs-lisp :tangle yes
(require 'cl-lib)
(require 'cl-extra)
#+end_src

** Functions
Prevent forms from producing output or other noise.
#+begin_src emacs-lisp :tangle yes
(defmacro quiet! (&rest forms)
  "Run FORMS without making any noise."
  `(if init-file-debug
       (progn ,@forms)
     (let ((message-log-max nil))
       (with-temp-message (or (current-message) "") ,@forms))))

(defun quiet-function-advice (orig-fn &rest args)
  "Advice used to make a function quiet.
Call ORIG-FN with ARGS and suppress the output.  Usage:

  (advice-add \\='orig-fn :around #\\='quiet-function-advice)"
  (quiet! (apply orig-fn args)))
#+end_src

Easily define REPL functions.
#+begin_src emacs-lisp :tangle yes
(declare-function comint-check-proc "comint")

(defmacro define-repl (fn-name buffer-name command &rest args)
  "Define a REPL function named FN-NAME running COMMAND inside BUFFER-NAME."
  (let ((repl-buffer (concat "*" buffer-name "*")))
    `(defun ,fn-name ()
       ,(format "Run an inferior instance of %s inside Emacs." command)
       (interactive)
       (let ((buffer (get-buffer-create ,repl-buffer)))
         (unless (comint-check-proc ,repl-buffer)
           (apply 'make-comint-in-buffer ,buffer-name buffer ,command nil ,args))
         (pop-to-buffer buffer)))))
#+end_src

*** Display
#+begin_src emacs-lisp :tangle yes
(defun display-ctrl-M-as-newline ()
  "Display `^M' as newline."
  (interactive)
  (setq buffer-display-table (make-display-table))
  (aset buffer-display-table ?\^M [?\n]))
#+end_src

*** Frame
Screenshot current frame in =.svg= or =.png= format.
#+begin_src emacs-lisp :tangle yes
(defun screenshot (type)
  "Save a screenshot of the current frame as an image in TYPE format.
Saves to a temp file and puts the filename in the kill ring."
  (let* ((ext (concat "." (symbol-name type)))
         (filename (make-temp-file "Emacs-" nil ext))
         (data (x-export-frames nil type)))
    (with-temp-file filename
      (insert data))
    (kill-new filename)
    (message filename)))

(defun screenshot-svg ()
  "Save a screenshot of the current frame as an SVG image.
Saves to a temp file and puts the filename in the kill ring."
  (interactive)
  (screenshot 'svg))

(defun screenshot-png ()
  "Save a screenshot of the current frame as an PNG image.
Saves to a temp file and puts the filename in the kill ring."
  (interactive)
  (screenshot 'png))
#+end_src

*** Process
#+begin_src emacs-lisp :tangle yes
(defun send-buffer-to-ssh ()
  "Send the whole buffer to the *ssh* process."
  (interactive)
  (process-send-region "*ssh*" (point-min) (point-max)))

(defun send-to-ssh ()
  "Send selected region or current line to the *ssh* process."
  (interactive)
  (let ((procbuf "*ssh*"))
    (if (use-region-p)
        (process-send-region procbuf (region-beginning) (region-end))
      (process-send-string procbuf (thing-at-point 'line t)))))
#+end_src

*** Window
Fringe control:
#+begin_src emacs-lisp :tangle yes
(define-minor-mode hide-fringes-mode
  "Toggle hiding of fringes."
  :group 'fringe
  (if hide-fringes-mode
      (set-window-fringes nil 0 0 nil)
    (set-window-fringes nil left-fringe-width right-fringe-width t)))

(define-minor-mode global-hide-fringes-mode
  "Toggle hiding of fringes globally."
  :global t
  :group 'fringe
  (if global-hide-fringes-mode
      (set-fringe-style 0)
    (set-fringe-style nil)))
#+end_src

** Localization
#+begin_src emacs-lisp :tangle yes
(setq-default calendar-week-start-day 1)
#+end_src

** Shell
Set shell to bash as fish is not compatible with all packages using the shell.
#+begin_src emacs-lisp :tangle yes
(setq shell-file-name "bash")
#+end_src

** Region
Fix WSL/WSLg copy issue.
#+begin_src emacs-lisp :tangle yes
(setq select-active-regions nil)
#+end_src

** UX
Disable bell (both visual and audible).
#+begin_src emacs-lisp :tangle yes
(setq ring-bell-function #'ignore)
(setq visible-bell nil)
#+end_src

Use =y= / =n= instead of =yes= / =no.=
#+begin_src emacs-lisp :tangle yes
(setq confirm-kill-emacs #'y-or-n-p)
(fset #'yes-or-no-p #'y-or-n-p)
#+end_src

Hide M-x commands which does not work in the current buffer.
#+begin_src emacs-lisp :tangle yes
(setq read-extended-command-predicate #'command-completion-default-include-p)
#+end_src

* Appearance
** Typography
*** Size
#+begin_src emacs-lisp :tangle yes
(defvar init-line-spacing 0.25
  "Spacing between lines.")

(defvar init-default-font-height 105
  "Default font height.")
#+end_src

*** Face
#+begin_src emacs-lisp :tangle yes
(defvar init-fixed-pitch-font "Iosevka Curly Slab"
  "Font used for fixed-pitch faces.")

(defvar init-variable-pitch-font "Iosevka Aile"
  "Font used for variable-pitch faces.")

(defvar init-serif-font "Iosevka Etoile"
  "Font used for serif faces.")

(defvar init-unicode-font "Noto Sans Mono"
  "Fallback font used for unicode glyphs.")

(defvar init-symbol-fonts '("Noto Emoji" "Symbola")
  "Fonts used for symbol/emoji faces.")
#+end_src

Setup symbol fonts.
#+begin_src emacs-lisp :tangle yes
(dolist (font init-symbol-fonts)
  (set-fontset-font t 'symbol font nil 'append))
#+end_src

*** Line length
#+begin_src emacs-lisp :tangle yes
(setq-default fill-column 110)
#+end_src

*** Underline
Underline line at descent position, not baseline position.
#+begin_src emacs-lisp :tangle yes
(setq x-underline-at-descent-line t)
#+end_src

** Theme
Use a bar cursor by default.
#+begin_src emacs-lisp :tangle yes
(setq-default cursor-type 'bar)
#+end_src

*** Readable typography
#+begin_src emacs-lisp :tangle yes
(use-package readable-typo-theme
  :ensure t
  :demand t
  :init
  (setq readable-typo-theme-default-font-height init-default-font-height)
  (setq readable-typo-theme-line-spacing init-line-spacing)
  (setq readable-typo-theme-fixed-pitch-font init-fixed-pitch-font)
  (setq readable-typo-theme-fixed-pitch-serif-font init-fixed-pitch-font)
  (setq readable-typo-theme-variable-pitch-font init-variable-pitch-font)
  (setq readable-typo-theme-serif-font init-serif-font)
  :config
  (load-theme 'readable-typo t))
#+end_src

*** Readable monochrome
#+begin_src emacs-lisp :tangle yes
(use-package readable-mono-theme
  :ensure t
  :demand t
  :config
  (load-theme 'readable-mono t))
#+end_src

*** Echo area
Attach a face to the echo area in order to style it differently.
#+begin_src emacs-lisp :tangle yes
(dolist (buffer-name '(" *Echo Area 0*"
                       " *Echo Area 1*"))
  (with-current-buffer (get-buffer-create buffer-name)
    (setq-local face-remapping-alist
                '((default readable-typo-theme-echo-area)))))
#+end_src

*** Functions
#+begin_src emacs-lisp :tangle yes
(defun toggle-theme-mode ()
  "Toggle dark/light `background-mode' and reload all loaded themes."
  (interactive)
  (customize-set-variable
   'frame-background-mode
   (if (eq 'light (frame-parameter nil 'background-mode)) 'dark 'light))
  (customize-set-variable 'custom-enabled-themes custom-enabled-themes))

(defun reload-themes ()
  "Reload all loaded themes."
  (interactive)
  (customize-set-variable 'custom-enabled-themes custom-enabled-themes))
#+end_src

** Layout
*** Frame
See [[info:elisp#Frame Layout][Frame Layout]] for details and terminology.
- Add some padding around the whole window (=internal-border-width=) to provide some air.
#+begin_src emacs-lisp :tangle yes
(cl-pushnew '(internal-border-width . 16) default-frame-alist :test #'equal)
#+end_src

*** Fringe
Setup fringes on both sides and display an indicator for buffer boundaries on the right
side. Display fringes outside margins to have the [[Padding][padding]] on the inside.
#+begin_src emacs-lisp :tangle yes
(setq-default fringes-outside-margins t
              left-fringe-width 8
              right-fringe-width 8
              indicate-buffer-boundaries 'right)
#+end_src

*** Padding
Add margins inside windows to make text feel less crowded. Padding around the frame is
configured via the =internal-border-width= in the [[Frame][Frame]] section.
#+begin_src emacs-lisp :tangle yes
(setq-default left-margin-width 1
              right-margin-width 1)
#+end_src

*** Borders
Add window dividers, mainly to add a border below the mode line.
#+begin_src emacs-lisp :tangle yes
(use-package frame
  :hook
  ((after-make-frame-functions
    server-after-make-frame-hook) . window-divider-mode)
  :init
  (setq window-divider-default-places t)
  (setq window-divider-default-bottom-width 1)
  (setq window-divider-default-right-width 1))
#+end_src

** Mode line
I try to retain functionality from the original mode line as much as possible, but I also
want to simplify it slightly and separate into two parts; left side and right side mode
line. There is a helper function in order to fill up the space needed to make the mode
line appear on the right side. Also the coding system related information will be hidden
if using utf-8-unix. I am also not displaying any minor mode lighters.

Define a variable to conveniently access only the major mode part of =mode-line-modes=.
#+begin_src emacs-lisp :tangle yes
(defvar mode-line-major-mode
  `(:propertize ("" mode-name)
                help-echo "Major mode\n\
mouse-1: Display major mode menu\n\
mouse-2: Show help for major mode\n\
mouse-3: Toggle minor modes"
                mouse-face mode-line-highlight
                local-map ,mode-line-major-mode-keymap))
#+end_src

Configure the order and components of the mode line.
#+begin_src emacs-lisp :tangle yes
(setq-default mode-line-format
              '("%e" mode-line-front-space
                mode-line-misc-info
                (vc-mode vc-mode)
                "  "
                mode-line-modified
                mode-line-remote
                mode-line-buffer-identification
                mode-line-position
                (:eval (mode-line-right))
                mode-line-end-spaces))
#+end_src

*** Right alignment
Setup the right aligned mode line and helper function to display it.
#+begin_src emacs-lisp :tangle yes
(defvar mode-line-right-format nil
  "The mode line to display on the right side.")

(defun mode-line-right ()
  "Render the `mode-line-right-format'."
  (let ((formatted-line (format-mode-line mode-line-right-format)))
    (list
     (propertize
      " "
      'display
      `(space :align-to (- (+ right right-fringe right-margin) ,(string-width formatted-line))))
     formatted-line)))
#+end_src

Move default components to the right side of the mode line.
#+begin_src emacs-lisp :tangle yes
(setq mode-line-right-format
      (list '(:eval mode-line-mule-info)
            "  "
            mode-line-major-mode))
#+end_src

*** Position
Add position information including column and line number but skip the percentage.
#+begin_src emacs-lisp :tangle yes
(setq mode-line-position-column-line-format '(" L%l:C%C"))
(setq mode-line-percent-position nil)
(column-number-mode 1)
(line-number-mode 1)
#+end_src

*** Coding system
To reduce unnecessary information coding system will not be shown by default if the file
is UTF-8 with UNIX end-of-line.
- Only display "end of line"-mnemonic when not UNIX end-of-line.
- Only display coding system when not UTF-8.
- Other cases displays either with warning/error face in order to draw attention.
#+begin_src emacs-lisp :tangle yes
(setq eol-mnemonic-unix ""
      eol-mnemonic-dos (propertize "[CR+LF]" 'face 'warning)
      eol-mnemonic-mac (propertize "[CR]" 'face 'warning)
      eol-mnemonic-undecided (propertize "[?]" 'face 'error))

(let ((coding (nthcdr 2 mode-line-mule-info)))
  (setcar coding '(:eval (if (string-equal "U" (format-mode-line "%z"))
                             ""
                           (propertize "[%z]" 'face 'warning))))
  coding)
#+end_src

*** Indentation
Display information about the current indentation settings.
#+begin_src emacs-lisp :tangle yes
(use-package indent-info
  :ensure t
  :hook
  (after-init-hook . global-indent-info-mode)
  :init
  (setq indent-info-display-change-message-p nil)
  (setq indent-info-insert-target 'mode-line-mule-info)
  (setq indent-info-space-format "Spaces: %s")
  (setq indent-info-tab-format "Tab Size: %s"))
#+end_src

*** Hide mode line
Support hiding the mode line, this can be useful for different modes displaying documents
or presentation.
#+begin_src emacs-lisp :tangle yes
(use-package hide-mode-line
  :ensure t
  :commands
  (hide-mode-line-mode
   turn-on-hide-mode-line-mode
   turn-off-hide-mode-line-mode))
#+end_src

*** Icons
**** Modified
#+begin_src emacs-lisp :tangle yes
(defun mode-line-modified-icons ()
  "Icon representation of `mode-line-modified'."
  (cond (buffer-read-only
         (concat (all-the-icons-octicon "lock" :v-adjust -0.05) " "))
        ((buffer-modified-p)
         (concat (all-the-icons-faicon "floppy-o" :v-adjust -0.05) " "))
        ((and buffer-file-name
              (not (file-exists-p buffer-file-name)))
         (concat (all-the-icons-octicon "circle-slash" :v-adjust -0.05) " "))))

(setq-default mode-line-modified '((:eval (mode-line-modified-icons))))
#+end_src

**** Remote
#+begin_src emacs-lisp :tangle yes
(defun mode-line-remote-icons ()
  "Icon representation of `mode-line-remote'."
  (when (and buffer-file-name
             (file-remote-p buffer-file-name))
    (concat (all-the-icons-octicon "radio-tower" :v-adjust -0.02) " ")))

(setq-default mode-line-remote   '((:eval (mode-line-remote-icons))))
#+end_src

**** VCS
Shorten long Git branch names as well as replace Git prefix with a nice icon.
#+begin_src emacs-lisp :tangle yes
(defun vc-git-mode-line-shorten (string)
  "Shorten `version-control' STRING in mode-line and add icon."
  (cond
   ((string-prefix-p "Git" string)
    (concat (all-the-icons-octicon "git-branch" :v-adjust -0.05)
            " "
            (if (> (length string) 30)
                (concat (substring-no-properties string 4 30) "…")
              (substring-no-properties string 4))))
   (t
    string)))
(advice-add 'vc-git-mode-line-string :filter-return #'vc-git-mode-line-shorten)
#+end_src

* Keybindings
** Leader keys
#+begin_src emacs-lisp :tangle yes
(defvar leader-key "C-,"
  "The key used for most custom operations.")
(defvar local-leader-key "C-."
  "The key used for major mode operations.")
#+end_src

** Prefixes
#+begin_src emacs-lisp :tangle yes
(defvar toggle-prefix "C-'"
  "Key prefix for commands related to toggling.")
(defvar next-prefix "M-]"
  "Key prefix used for commands doing a next operation.")
(defvar prev-prefix "M-["
  "Key prefix used for commands doing a previous operation.")
#+end_src

** Repeat
#+begin_src emacs-lisp :tangle yes
(use-package repeat
  :hook
  (after-init-hook . repeat-mode))
#+end_src

** Global
#+begin_src emacs-lisp :tangle yes
;; Editing
(keymap-global-set "<XF86Tools>" #'just-one-space)
(keymap-global-set "C-M-y" #'duplicate-dwim)
(keymap-global-set "C-x C-/" #'revert-buffer)
(keymap-global-set "C-z" #'zap-up-to-char)
(keymap-global-set "<remap> <upcase-word>" #'upcase-dwim)
(keymap-global-set "<remap> <downcase-word>" #'downcase-dwim)
(keymap-global-set "<remap> <capitalize-word>" #'capitalize-dwim)
;; Files
(keymap-global-set "C-x j" #'find-sibling-file)
;; Region
(keymap-global-set "C-x r S" #'sort-lines)
#+end_src

** Navigation
#+begin_src emacs-lisp :tangle yes
(keymap-set goto-map "k" #'eldoc-doc-buffer)
#+end_src

** Window
#+begin_src emacs-lisp :tangle yes
(use-package window
  :bind
  ((:map window-prefix-map
         ("d" . toggle-dedicated-window)
         ("m" . maximize-window)
         ("r" . window-configuration-to-register)
         ("w" . window-toggle-side-windows))
   (:repeat-map resize-window-repeat-map
                ("+" . balance-windows)
                ("-" . fit-window-to-buffer)
                ("m" . maximize-window))
   (:repeat-map buffer-repeat-map
                ("b" . next-buffer)
                ("B" . previous-buffer)
                ("[" . previous-buffer)
                ("]" . next-buffer))
   (:repeat-map other-window-repeat-map
                ("o" . other-window)
                ("O" . other-window-reverse-repeat)))
  :config
  (defun toggle-dedicated-window ()
    "Toggle selected window as dedicated window."
    (interactive)
    (set-window-dedicated-p (selected-window)
                            (not (window-dedicated-p (selected-window)))))

  (defun other-window-reverse-repeat ()
    (interactive)
    (setq repeat-map 'other-window-repeat-map)
    (other-window -1)))
#+end_src

** Next/Previous
#+begin_src emacs-lisp :tangle yes
(defvar-keymap next-map
  :doc "Keymap for the next key sequences."
  :prefix 'next-map-prefix
  "b" '("Buffer" . next-buffer))
(keymap-global-set next-prefix 'next-map-prefix)

(defvar-keymap prev-map
  :doc "Keymap for the prev key sequences."
  :prefix 'prev-map-prefix
  "b" '("Buffer" . previous-buffer))
(keymap-global-set prev-prefix 'prev-map-prefix)
#+end_src

** Toggle
#+begin_src emacs-lisp :tangle yes
(keymap-global-set toggle-prefix 'toggle-map-prefix)
(defvar-keymap toggle-map
  :doc "Keymap for toggle key sequences."
  :prefix 'toggle-map-prefix
  "*" '("Light/Dark theme" . toggle-theme-mode)
  "c" '("Changes" . highlight-changes-mode)
  "d" '("Debug on error" . toggle-debug-on-error)
  "f" '("Code folding" . hs-minor-mode)
  "F" '("Follow" . follow-mode)
  ;; Group together as one mode?
  "g" '("Sub-word" . subword-mode)
  "G" '("Readable camelCase" . glasses-mode)
  "h" '("Line highlight" . hl-line-mode)
  "l" '("Line numbers" . global-display-line-numbers-mode)
  "t" '("Truncate lines" . toggle-truncate-lines)
  "V" '("Variable-pitch" . variable-pitch-mode)
  "w" '("White-space" . whitespace-mode)
  "x" '("Syntax checker" . flymake-mode))
#+end_src

** Leader
#+begin_src emacs-lisp :tangle yes
(keymap-global-set leader-key 'leader-map-prefix)
(defvar-keymap leader-map
  :doc "Keymap for leader key sequences."
  :prefix 'leader-map-prefix
  "m" 'gnus
  "P" 'list-processes
  "s" 'screenshot-svg
  "S" 'screenshot-png
  "x" 'regexp-builder
  "w" 'eww)

(keymap-global-set local-leader-key 'local-leader-map-prefix)
(defvar-keymap local-leader-map
  :doc "Keymap for leader key sequences."
  :prefix 'local-leader-map-prefix)
#+end_src

* Completion
Enable indentation and completion with the =TAB= key.
#+begin_src emacs-lisp :tangle yes
(setq tab-always-indent 'complete)
#+end_src

Cycle with the =TAB= key if there are only few candidates.
#+begin_src emacs-lisp :tangle yes
(setq completion-cycle-threshold 3)
#+end_src

** Preview
Completion preview inline, with quick completion key.
#+begin_src emacs-lisp :tangle yes
(use-package completion-preview
  :hook
  ((comint-mode-hook
    eshell-mode-hook
    prog-mode-hook
    text-mode-hook) . completion-preview-mode)
  (minibuffer-setup-hook . completion-preview-enable-in-minibuffer)
  :bind
  (:map completion-preview-active-mode-map
        ("TAB" . completion-preview-complete)
        ("C-e" . completion-preview-insert))
  :init
  (setq completion-preview-adapt-background-color nil)
  (setq completion-preview-minimum-symbol-length 2)
  :config
  (defun completion-preview-enable-in-minibuffer ()
    "Enable Corfu completion in the minibuffer, e.g., `eval-expression'."
    (when (where-is-internal #'completion-at-point (list (current-local-map)))
      (completion-preview-mode 1)))

  (cl-pushnew 'org-self-insert-command completion-preview-commands :test #'equal))
#+end_src

** Style
*** orderless
Space-separated matching components matching in any order.
#+begin_src emacs-lisp :tangle yes
(use-package orderless
  :ensure t
  :init
  (setq completion-styles '(orderless basic))
  (setq completion-category-defaults nil)
  (setq completion-category-overrides '((file (styles partial-completion)))))
#+end_src

** Buffer
*** corfu
Completion Overlay Region FUnction.
#+begin_src emacs-lisp :tangle yes
(use-package corfu
  :ensure t
  :commands
  (corfu-mode
   corfu-indexed-mode
   global-corfu-mode)
  :hook
  (minibuffer-setup-hook . corfu-enable-in-minibuffer)
  (corfu-mode-hook . corfu-popupinfo-mode)
  :bind
  (:map corfu-map
        ([return] . nil)
        ("RET" . nil)
        ("TAB" . corfu-next)
        ([tab] . corfu-next)
        ("S-TAB" . corfu-previous)
        ([backtab] . corfu-previous)
        ("C-e" . corfu-complete))
  :init
  (setq corfu-cycle t)
  (setq corfu-preselect 'first)
  (global-corfu-mode 1)
  (corfu-indexed-mode 1)
  :config
  (defun corfu-enable-in-minibuffer ()
    "Enable Corfu completion in the minibuffer, e.g., `eval-expression'."
    (when (where-is-internal #'completion-at-point (list (current-local-map)))
      (corfu-mode 1))))
#+end_src

** Minibuffer
*** vertico
Vertical interactive completion UI.
#+begin_src emacs-lisp :tangle yes
(use-package vertico
  :ensure t
  :commands
  (vertico-insert
   vertico-exit)
  :hook
  (after-init-hook . vertico-mode)
  :bind
  (:map vertico-map
        ("C-e" . vertico-move-end-of-line-or-insert))
  :init
  (defun vertico-move-end-of-line-or-insert (arg)
    "Move to end of line or insert current candidate.
   ARG lines can be used.

   When only one candidate exists exit input after insert."
    (interactive "p")
    (if (eolp)
        (progn
          (vertico-insert)
          (when (= vertico--total 1)
            (vertico-exit)))
      (move-end-of-line arg))))
#+end_src

** Extensions
*** all-the-icons-completion
#+begin_src emacs-lisp :tangle yes
(use-package all-the-icons-completion
  :ensure t
  :hook
  (marginalia-mode-hook . all-the-icons-completion-marginalia-setup))
#+end_src

*** cape
Completion At Point Extensions.
#+begin_src emacs-lisp :tangle yes
(use-package cape
  :ensure t
  :commands
  (cape-wrap-silent
   cape-wrap-purify)
  :functions
  (cape-capf-buster
   cape-capf-super
   cape-capf-interactive)
  :hook
  ((comint-mode-hook
    eshell-mode-hook) . init-cape-comint-capf)
  (eglot-managed-mode-hook . init-cape-eglot-capf)
  (prog-mode-hook . init-cape-prog-capf)
  (text-mode-hook . init-cape-text-capf)
  :bind
  (([remap dabbrev-expand] . cape-dabbrev)
   (:prefix-map completion-prefix-map :prefix "M-p"
                ("d" . cape-dabbrev)
                ("h" . cape-history)
                ("f" . cape-file)
                ("k" . cape-keyword)
                ("M-p" . completion-at-point)
                ("p" . cape-pcomplete)
                ("s" . cape-elisp-symbol)
                ("a" . cape-abbrev)
                ("l" . cape-line)
                ("w" . cape-dict)
                ("\\" . cape-tex)
                ("&" . cape-sgml)
                ("r" . cape-rfc1345))
   (:map corfu-map
         ("C-x C-f" . cape-file)))
  :init
  ;; Use cape-dict for dictionary completion.
  (setq cape-dict-file (getenv "WORDLIST"))
  (setq text-mode-ispell-word-completion nil)
  :config
  (declare-function cl-remove-if-not "cl-lib")
  (declare-function pcomplete-completions-at-point "pcomplete")

  (defun init-cape-comint-capf ()
    (setq-local completion-at-point-functions
                (list (apply #'cape-capf-super
                             #'cape-history
                             (cl-remove-if-not #'functionp completion-at-point-functions)))))

  (defun init-cape-eglot-capf ()
    (setq-local completion-at-point-functions
                (list #'cape-file
                      (cape-capf-super (cape-capf-buster #'eglot-completion-at-point #'string-prefix-p)
                                       :with #'tempel-complete))))

  (defun init-cape-prog-capf ()
    (add-hook 'completion-at-point-functions #'cape-file nil t))

  (defun init-cape-text-capf ()
    (add-hook 'completion-at-point-functions #'cape-file nil t)
    (add-hook 'completion-at-point-functions #'cape-dict 10 t))

  (setq-default completion-at-point-functions
                (list #'cape-dabbrev))

  (setf (symbol-function 'cape-pcomplete) (cape-capf-interactive #'pcomplete-completions-at-point))
  (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent)
  (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify))
#+end_src

*** consult
Commands compatible with ~completing-read~.
#+begin_src emacs-lisp :tangle yes
(use-package consult
  :ensure t
  :bind
  (([remap bookmark-jump] . consult-bookmark)
   ([remap goto-line] . consult-goto-line)
   ([remap imenu] . consult-imenu)
   ([remap jump-to-register] . consult-register)
   ([remap point-to-register] . consult-register-store)
   ([remap switch-to-buffer] . consult-buffer)
   ([remap yank-pop] . consult-yank-pop)
   ("C-c k" . consult-kmacro)
   ("C-x C-r" . consult-recent-file)
   (:map search-map
         ("g" . consult-ripgrep)
         ("m" . consult-mark)
         ("M-m" . consult-global-mark)))
  :init
  (setq consult-preview-key "M-RET"))

(use-package consult-xref
  :after xref
  :functions
  (consult-xref)
  :init
  (setq xref-show-xrefs-function #'consult-xref)
  (setq xref-show-definitions-function #'consult-xref))
#+end_src

*** dabbrev
#+begin_src emacs-lisp :tangle yes
(use-package dabbrev
  :init
  (setq dabbrev-case-replace nil)
  (setq dabbrev-ignored-buffer-regexps '("\\.\\(?:pdf\\|jpe?g\\|png\\)\\'")))
#+end_src

*** kind-icon
Icons for buffer completions.
#+begin_src emacs-lisp :tangle no
(use-package kind-icon
  :ensure t
  :after corfu
  :commands
  (kind-icon-margin-formatter)
  :init
  (cl-pushnew #'kind-icon-margin-formatter corfu-margin-formatters :test #'equal))
#+end_src

*** marginalia
Annotations for completion candidates.
#+begin_src emacs-lisp :tangle yes
(use-package marginalia
  :ensure t
  :hook
  (after-init-hook . marginalia-mode))
#+end_src

* Help
#+begin_src emacs-lisp :tangle yes
(use-package help
  :bind
  (:map help-map
        ("B" . find-library)
        ("u" . describe-face)
        ("U" . list-faces-display)
        ("'" . describe-char)))

(use-package help-at-pt
  :init
  (setq help-at-pt-timer-delay 0.1)
  (setq help-at-pt-display-when-idle '(flymake-diagnostic)))
#+end_src

** ghelp
Generic help system.
#+begin_src emacs-lisp :tangle yes
(use-package ghelp
  :ensure t
  :bind
  ((:map help-map
         ("A" . ghelp-describe-elisp)
         ("f" . ghelp-describe-function)
         ("k" . ghelp-describe-key)
         ("v" . ghelp-describe-variable))
   (:map goto-map
         ("h" . ghelp-describe-at-point))))
#+end_src

** helpful
Better =*help*= buffer.
#+begin_src emacs-lisp :tangle yes
(use-package helpful
  :ensure t
  :bind
  (:map help-map
        ("C" . helpful-command)
        ("M" . helpful-macro)))
#+end_src

* Display
** Window
Favor horizontal splits.
#+begin_src emacs-lisp :tangle yes
(setq split-width-threshold nil)
#+end_src

Respect display actions for both automatic and manual window switching.
#+begin_src emacs-lisp :tangle yes
(setq switch-to-buffer-obey-display-actions t)
#+end_src

Resize pixelwise.
#+begin_src emacs-lisp :tangle yes
(setq window-resize-pixelwise t)
#+end_src

Hide asynchronous shell command buffers.
#+begin_src emacs-lisp :tangle yes
(cl-pushnew '("^*Async Shell Command*" . (display-buffer-no-window))
            display-buffer-alist
            :test #'equal)
#+end_src

*** Manage layouts
Undo/redo between window layouts.
#+begin_src emacs-lisp :tangle yes
(use-package winner
  :hook
  (window-setup-hook . winner-mode)
  :bind
  (:repeat-map winner-repeat-map
               ("[" . winner-undo)
               ("]" . winner-redo)
               ("M-[" . winner-undo)
               ("M-]" . winner-redo))
  :init
  (setq winner-dont-bind-my-keys t)
  (keymap-set next-map next-prefix '("Window History" . winner-redo))
  (keymap-set prev-map prev-prefix '("Window History" . winner-undo)))
#+end_src

Transpose window arrangement.
#+begin_src emacs-lisp :tangle yes
(use-package transpose-frame
  :ensure t
  :bind
  ((:map window-prefix-map
         ("t" . transpose-frame)
         ("f" . flip-frame)
         ("F" . flop-frame))
   (:repeat-map window-arrangement-repeat-map
                ("t" . transpose-frame)
                ("f" . flip-frame)
                ("F" . flop-frame))))
#+end_src

Manage "popups".
#+begin_src emacs-lisp :tangle yes
(use-package popper
  :ensure t
  :commands
  (popper-display-popup-at-bottom)
  :hook
  (after-init-hook . popper-mode)
  (popper-mode-hook . popper-echo-mode)
  :bind
  (("C-`" . popper-toggle)
   ("C-M-`" . popper-cycle)
   (:map window-prefix-map
         ("p" . popper-toggle-type)))
  :init
  (setq popper-display-function #'popper-display-popup-at-bottom)
  (setq popper-reference-buffers
        '("Output\\*$"
          "\\*Messages\\*"
          "\\*Warnings\\*"
          "\\*devdocs\\*"
          "\\*envrc\\*"
          "^\\*eldoc"
          elisp-refs-mode
          flymake-diagnostics-buffer-mode
          ghelp-page-mode
          (lambda (buf)
            (with-current-buffer buf
              (and (derived-mode-p '(compilation-mode
                                     comint-mode
                                     help-mode))
                   (not (derived-mode-p 'opencode-session-mode))))))))
#+end_src

** Buffer
*** Cursor
#+begin_src emacs-lisp :tangle yes
(define-minor-mode hide-cursor-mode
  "Toggle hiding of the cursor."
  :lighter " Hide Cursor"
  (if hide-cursor-mode
      (setq cursor-type nil)
    (kill-local-variable 'cursor-type)))
#+end_src

*** Buttons
Buttonize URLs and e-mail addresses in the current buffer.
#+begin_src emacs-lisp :tangle yes
(use-package goto-addr
  :hook
  (text-mode-hook . goto-address-mode)
  (prog-mode-hook . goto-address-prog-mode))
#+end_src

*** Line highlight
Disabled by default. When enabled, only highlight in the selected window.
#+begin_src emacs-lisp :tangle yes
(use-package hl-line
  :hook
  (hl-line-mode-hook . hl-line-number-mode)
  (global-hl-line-mode-hook . global-hl-line-number-mode)
  :init
  (setq hl-line-sticky-flag nil)
  (setq global-hl-line-sticky-flag nil)
  :preface
  (autoload 'face-remap-add-relative "face-remap")
  (autoload 'face-remap-remove-relative "face-remap")

  (defvar-local hl-line-number--current-line-remap nil
    "Face remapping cookie for `line-number-current-line' to `hl-line'.")

  (define-globalized-minor-mode global-hl-line-number-mode hl-line-number-mode hl-line-number-mode
    :group 'hl-line)

  (define-minor-mode hl-line-number-mode
    "Toggle highlighting of the current line number."
    :group 'hl-line
    (if (or (bound-and-true-p global-hl-line-mode) (bound-and-true-p hl-line-mode))
        (setq hl-line-number--current-line-remap
              (face-remap-add-relative 'line-number-current-line 'hl-line))
      (face-remap-remove-relative hl-line-number--current-line-remap)
      (setq hl-line-number--current-line-remap nil))))
#+end_src

*** Line wrapping
**** adaptive-wrap
#+begin_src emacs-lisp :tangle yes
(use-package adaptive-wrap
  :ensure t
  :hook
  (prog-mode-hook . adaptive-wrap-prefix-mode))
#+end_src

**** Visual Fill Column
Soft wrap lines according to =fill-column= in =visual-line-mode=.
#+begin_src emacs-lisp :tangle yes
(use-package visual-fill-column
  :ensure t
  :init
  (setq visual-fill-column-center-text t))

(use-package virtual-auto-fill
  :ensure t
  :commands
  (virtual-auto-fill-mode)
  :init
  (keymap-set toggle-map "SPC" '("Virtual auto fill" . virtual-auto-fill-mode)))
#+end_src

*** Page breaks
Display page breaks as a horizontal line.
#+begin_src emacs-lisp :tangle yes
(use-package page-break-lines
  :ensure t
  :defer 3
  :commands
  (global-page-break-lines-mode)
  :config
  (global-page-break-lines-mode 1))
#+end_src

*** Tables
#+begin_src emacs-lisp :tangle yes
(use-package valign
  :ensure t
  :hook
  ((markdown-mode-hook
    org-mode-hook) . valign-mode)
  :init
  (setq valign-fancy-bar t))
#+end_src

Truncate large tables.
#+begin_src emacs-lisp :tangle yes
(use-package lte
  :ensure t
  :hook
  ((markdown-mode-hook
    org-mode-hook) . lte-truncate-table-mode))
#+end_src

*** Visible
Making invisible text temporarily visible.
#+begin_src emacs-lisp :tangle yes
(use-package simple
  :commands
  (visible-mode)
  :init
  (keymap-set toggle-map "v" '("Visibility" . visible-mode)))
#+end_src

*** Whitespace
Display whitespace.
#+begin_src emacs-lisp :tangle yes
(use-package whitespace
  :init
  (setq whitespace-line-column fill-column)
  (setq whitespace-style
        '(face tabs tab-mark spaces space-mark trailing lines-tail))
  (setq whitespace-display-mappings
        '((tab-mark 9 [8250 9])
          (newline-mark 10 [172 10])
          (space-mark 32 [183] [46]))))
#+end_src

* Editing
** Indentation
Convert between tabs and spaces (only tabify initial white-space).
#+begin_src emacs-lisp :tangle yes
(setq-default tabify-regexp "^\t* [ \t]+")
#+end_src

** Text flow
Wrap at words.
#+begin_src emacs-lisp :tangle yes
(setq-default word-wrap t)
#+end_src

** Kill-ring
Save clipboard contents into kill-ring before replacing them.
#+begin_src emacs-lisp :tangle yes
(setq save-interprogram-paste-before-kill t)
(setq kill-ring-max 30)
#+end_src

** Parenthesis
Automatic parenthesis pairing and highlighting.
#+begin_src emacs-lisp :tangle yes
(electric-pair-mode 1)
(show-paren-mode 1)
(setq show-paren-context-when-offscreen t)
#+end_src

** Auto-typing
*** tempel
#+begin_src emacs-lisp :tangle yes
(use-package tempel
  :ensure t
  :commands
  (tempel-expand)
  :bind
  (("M-+" . tempel-complete)
   ("M-*" . tempel-insert))
  :config
  (setq tempel-path (concat user-emacs-directory "templates")))
#+end_src

** Packages
*** dtrt-indent
Guess indentation offset and adjust the corresponding setting.
#+begin_src emacs-lisp :tangle yes
(use-package dtrt-indent
  :ensure t
  :hook
  ((prog-mode-hook
    text-mode-hook) . dtrt-indent-mode)
  :init
  (setq dtrt-indent-ignore-single-chars-flag t)
  (setq dtrt-indent-run-after-smie t)
  (setq dtrt-indent-verbosity 0)
  :config
  (cl-pushnew '(default default (standard-indent tab-width)) dtrt-indent-hook-mapping-list :test #'equal)
  (cl-pushnew '(groovy-mode default tab-width) dtrt-indent-hook-mapping-list :test #'equal)
  (cl-pushnew '(js-ts-mode javascript js-indent-level) dtrt-indent-hook-mapping-list :test #'equal))
#+end_src

*** grugru
Cycle through words, symbols and patterns.
#+begin_src emacs-lisp :tangle yes
(use-package grugru
  :ensure t
  :commands
  (grugru-forward
   grugru-backward)
  :functions
  (grugru-define-global
   grugru-define-on-major-mode)
  :bind
  (:repeat-map grugru-repeat-map
               ("r" . grugru-forward)
               ("R" . grugru-backward)
               ("SPC" . grugru-select)
               ("[" . grugru-backward)
               ("]" . grugru-forward))
  :init
  (keymap-set next-map "r" '("Rotate text" . grugru-forward))
  (keymap-set prev-map "r" '("Rotate text" . grugru-backward))
  :config
  (grugru-define-global 'symbol '("assert" "refute"))
  (grugru-define-global 'symbol '("true" "false"))
  (grugru-define-global 'symbol '("yes" "no")))

(use-package grugru-default
  :after grugru
  :functions grugru-default-setup
  :init (grugru-default-setup))
#+end_src

*** puni
Structured editing with soft deletion and balanced expressions.
#+begin_src emacs-lisp :tangle yes
(use-package puni
  :ensure t
  :commands
  (puni-mode
   puni-contract-region
   puni-expand-region
   puni-kill-line
   puni-kill-active-region
   puni-mark-list-around-point)
  :functions
  (puni-soft-delete
   puni--kill-line-bol-cleanup)
  :hook
  (after-init-hook . puni-global-mode)
  :bind
  ((:map puni-mode-map
         ([remap transpose-sexps] . puni-transpose)
         ("M-W" . puni-kill-ring-save-line)
         ("M-'" . puni-kill-list-around-point)
         ("M-D" . puni-splice)
         ("M-R" . puni-squeeze)
         ("M-S <" . puni-wrap-angle)
         ("M-S {" . puni-wrap-curly)
         ("M-S (" . puni-wrap-round)
         ("M-S [" . puni-wrap-square)
         ("C-w" . puni-whole-line-or-region-kill)
         ("M-w" . puni-whole-line-or-region-kill-ring-save)
         ("M-;" . puni-comment-line))
   (:repeat-map puni-region-repeat-map
                ("SPC" . puni-expand-region)
                ("[" . puni-contract-region)
                ("]" . puni-expand-region)))
  :init
  (keymap-set toggle-map toggle-prefix '("Structured editing" . puni-mode))
  (keymap-set next-map "SPC" '("Expand region" . puni-expand-region))
  (keymap-set prev-map "SPC" '("Contract region" . puni-contract-region))
  :config
  (defun puni--kill-line-bol-cleanup (orig-fn &rest args)
    "Advice for `puni-kill-line' to clean up a lone punctuation remnant.
When called from the start of a line's content, puni may leave a lone
punctuation char (e.g. \";\" in Nix) behind.  Delete it silently."
    (if (looking-back (rx line-start (* space)) (line-beginning-position))
        (let ((bol (line-beginning-position)))
          (apply orig-fn args)
          (when (save-excursion
                  (goto-char bol)
                  (looking-at-p (rx (* space) (+ (syntax punctuation)) (* space) line-end)))
            (delete-region bol (min (1+ (save-excursion (goto-char bol) (line-end-position)))
                                    (point-max))))
          (back-to-indentation))
      (apply orig-fn args)))

  (advice-add 'puni-kill-line :around #'puni--kill-line-bol-cleanup)

  (defun puni-comment-line (&optional n)
    "Toggle comment on the Puni-structural line(s).
With a region active, comment/uncomment the region. With prefix N,
operate on N structural lines. Uses puni's structural region when
commenting; when uncommenting, finds the contiguous commented block."
    (interactive "P")
    (if (use-region-p)
        (comment-or-uncomment-region (region-beginning) (region-end))
      (when n (setq n (prefix-numeric-value n)))
      (if (and comment-start
               (save-excursion
                 (skip-syntax-forward " ")
                 (looking-at (regexp-quote (string-trim comment-start)))))
          ;; Uncommenting: find the extent of the commented block and uncomment it.
          (let ((block-start
                 (if (comment-only-p (line-beginning-position) (line-end-position))
                     ;; Full comment line: scan backward through contiguous block.
                     (save-excursion
                       (while (and (not (bobp))
                                   (save-excursion
                                     (forward-line -1)
                                     (comment-only-p (line-beginning-position)
                                                     (line-end-position))))
                         (forward-line -1))
                       (line-beginning-position))
                   ;; Mid-line comment: start at the comment character.
                   (save-excursion (skip-syntax-forward " ") (point))))
                (block-end
                 (save-excursion
                   (forward-line 1)
                   (while (and (not (eobp))
                               (comment-only-p (line-beginning-position)
                                               (line-end-position)))
                     (forward-line 1))
                   (point))))
            (uncomment-region block-start block-end))
        ;; Commenting: use puni structural region for balanced commenting.
        (let ((pt (point))
              (to (save-excursion (forward-line (or n 1)) (point))))
          (unless (or (and kill-whole-line (bolp)) (eolp) n)
            (setq to (1- to)))
          (when-let* ((region (puni-soft-delete pt to 'strict-sexp 'beyond
                                                nil nil 'return-region)))
            ;; Extend region to include a trailing punctuation char (e.g. ";" in Nix).
            (let ((end (save-excursion
                         (goto-char (cdr region))
                         (skip-chars-forward " \t")
                         (if (and (not (eolp))
                                  (eq (char-syntax (char-after)) ?.))
                             (1+ (point))
                           (cdr region)))))
              (comment-region (car region) end)))))))

  (defun puni-kill-ring-save-line (arg)
    "Save whole structured line(s) based on ARG or active region."
    (interactive "P")
    (save-excursion
      (cl-letf (((symbol-function 'kill-region) #'kill-ring-save)
                ((symbol-function 'delete-region) #'ignore))
        (puni-kill-line arg))))

  (defun puni-whole-line-or-region-kill (arg)
    "Kill whole structured line(s) based on ARG or active region."
    (interactive "P")
    (if (use-region-p)
        (puni-kill-active-region)
      (let ((kill-whole-line t))
        (if (and (looking-at-p "^$") (= (or arg 1) 1))
            (kill-line)
          (save-excursion
            (beginning-of-line)
            (puni-kill-line arg))))))

  (defun puni-whole-line-or-region-kill-ring-save (arg)
    "Save whole structured line(s) based on ARG or active region."
    (interactive "P")
    (save-excursion
      (cl-letf (((symbol-function 'kill-region) #'kill-ring-save)
                ((symbol-function 'delete-region) #'ignore))
        (puni-whole-line-or-region-kill arg))))

  (defun puni-kill-list-around-point ()
    (interactive)
    (puni-mark-list-around-point)
    (kill-region nil nil t)))
#+end_src

*** separedit
#+begin_src emacs-lisp :tangle yes
(use-package separedit
  :ensure t
  :bind
  (:map prog-mode-map ("C-c '" . separedit))
  :init
  (setq separedit-preserve-string-indentation t))
#+end_src

*** string-inflection
Conversion between different variable naming conventions.
Toggle between snake/pascal/camel/up/kebab-case or capital underscore.
#+begin_src emacs-lisp :tangle yes
(use-package string-inflection
  :ensure t
  :bind
  ("M-_" . string-inflection-all-cycle))
#+end_src

*** visual-replace
Visually indicate what will be modified in a search and replace as you type.
#+begin_src emacs-lisp :tangle yes
(use-package visual-replace
  :ensure t
  :hook
  (after-init-hook . visual-replace-global-mode)
  :bind
  ([remap query-replace-regexp] . visual-replace-regexp)
  :config
  (defun visual-replace-regexp ()
    (interactive)
    (let ((visual-replace-defaults-hook 'visual-replace-toggle-regexp))
      (call-interactively 'visual-replace))))
#+end_src

* Buffers
Prevent generation of useless lock and backup files.
#+begin_src emacs-lisp :tangle yes
(setq create-lockfiles nil)
(setq make-backup-files nil)
#+end_src

Remove visual indicators from non-selected windows
#+begin_src emacs-lisp :tangle yes
(setq highlight-nonselected-windows nil)
(setq-default cursor-in-non-selected-windows nil)
#+end_src

** Auto save
#+begin_src emacs-lisp :tangle yes
(use-package files
  :hook
  (after-init-hook . auto-save-visited-mode)
  :init
  (setq auto-save-no-message t)
  (setq save-abbrevs 'silently))
#+end_src

** Auto executable scripts
#+begin_src emacs-lisp :tangle yes
(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
#+end_src

** Minibuffer
Enable recursive minibuffers.
#+begin_src emacs-lisp :tangle yes
(setq enable-recursive-minibuffers t)
#+end_src

Do not allow the cursor in the minibuffer prompt.
#+begin_src emacs-lisp :tangle yes
(setq minibuffer-prompt-properties
      '(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
#+end_src

Give some more room to the minbuffer.
#+begin_src emacs-lisp :tangle yes
(setq max-mini-window-height 0.3)
(setq resize-mini-windows 'grow-only)
#+end_src

*** No fringes
Don't show fringes in the minibuffer.
#+begin_src emacs-lisp :tangle yes
(defun disable-minibuffer-window-fringes (&rest _)
  "Disable the window fringes for minibuffer window."
  (set-window-fringes (minibuffer-window) 0 0 nil))
(add-hook 'minibuffer-setup-hook #'disable-minibuffer-window-fringes)
#+end_src

*** History
Track minibuffer history
#+begin_src emacs-lisp :tangle yes
(setq history-delete-duplicates t)
(setq history-length 500)
#+end_src

*** embark
#+begin_src emacs-lisp :tangle yes
(use-package embark
  :ensure t
  :commands
  (embark-prefix-help-command)
  :bind
  (("C-h B" . embark-bindings)
   ("M-o" . embark-dwim)
   ("M-O" . embark-act)
   (:map vertico-map ("C-SPC" . embark-select)))
  :init
  (setq prefix-help-command #'embark-prefix-help-command)
  (setq embark-indicators
        '(embark-minimal-indicator
          embark-highlight-indicator
          embark-isearch-highlight-indicator)))

(use-package embark-consult :ensure t)
#+end_src

*** Miniedit
Edit minibuffer in a new temporary buffer by pressing =C-c '=.
#+begin_src emacs-lisp :tangle yes
(use-package miniedit
  :ensure t
  :bind
  (:map minibuffer-local-map ("C-c '" . miniedit))
  :init
  (setq miniedit-show-help-p nil))
#+end_src

** Scrolling
#+begin_src emacs-lisp :tangle yes
(setq scroll-conservatively 101)
(setq scroll-preserve-screen-position t)
#+end_src

*** ultra-scroll
#+begin_src emacs-lisp :tangle yes
(use-package ultra-scroll
  :ensure t
  :hook
  (after-init-hook . ultra-scroll-mode))
#+end_src

** Packages
*** autorevert
Revert buffers when underlying files change.
#+begin_src emacs-lisp :tangle yes
(use-package autorevert
  :hook
  (image-mode-hook . auto-revert-mode)
  :init
  (setq auto-revert-verbose nil))
#+end_src

*** eldoc
#+begin_src emacs-lisp :tangle yes
(use-package eldoc
  :hook
  (after-init-hook . global-eldoc-mode))
#+end_src

*** epithet
Descriptive buffer names or project relative paths for buffer names.
#+begin_src emacs-lisp :tangle yes
(use-package epithet
  :ensure t
  :functions
  (epithet-relative-project-name)
  :hook
  ((eww-after-render-hook
    help-mode-hook
    Info-selection-hook
    occur-mode-hook
    prog-mode-hook
    shell-mode-hook
    text-mode-hook) . epithet-rename-buffer)
  :config
  (defun epithet-relative-project-name ()
    (when-let* ((project (project-current))
                (buffer-file-name (buffer-file-name)))
      (let* ((project-root (expand-file-name (project-root project)))
             (prefix (file-name-directory (directory-file-name project-root)))
             (buffer (expand-file-name buffer-file-name)))
        (string-trim buffer prefix))))

  (cl-pushnew #'epithet-relative-project-name epithet-suggesters :test #'equal))
#+end_src

*** pairable
#+begin_src emacs-lisp :tangle yes
(use-package pairable
  :ensure t
  :init
  (keymap-set toggle-map "p" '("Pair-programming" . pairable-mode)))
#+end_src

*** readable
#+begin_src emacs-lisp :tangle yes
(use-package readable
  :ensure t
  :hook
  ((eww-mode-hook
    Info-mode-hook
    nov-mode-hook
    text-mode-hook) . readable-mode))
#+end_src

* Navigation
Allow repeated mark popping. This behavior is similar to Vim's =C-o=. With this
configuration you can press =C-u= and continuously =C-SPC= to jump to previous entries in the
mark ring.
#+begin_src emacs-lisp :tangle yes
(setq set-mark-command-repeat-pop t)
#+end_src

** Dired
#+begin_src emacs-lisp :tangle yes
(use-package dired
  :hook
  (dired-mode-hook . auto-revert-mode)
  (dired-mode-hook . hl-line-mode)
  (dired-mode-hook . dired-hide-details-mode)
  :init
  (setq dired-listing-switches "-al --group-directories-first")
  ;; Always copy/delete recursively
  (setq dired-recursive-copies  'always)
  (setq dired-recursive-deletes 'top))
#+end_src

*** Editable
#+begin_src emacs-lisp :tangle yes
(use-package wdired
  :after dired
  :bind
  (:map dired-mode-map ("C-c '" . wdired-change-to-wdired-mode)))
#+end_src

*** Search
#+begin_src emacs-lisp :tangle yes
(use-package find-dired
  :bind
  ("C-x D" . find-dired)
  :init
  (setq find-ls-option '("-print0 | xargs -0 ls -ld" . "-ld")))
#+end_src

*** Sidebar
#+begin_src emacs-lisp :tangle yes
(use-package dired-sidebar
  :ensure t
  :hook
  (dired-sidebar-mode-hook . hide-mode-line-mode)
  (dired-sidebar-mode-hook . hl-line-mode)
  (dired-sidebar-mode-hook . variable-pitch-mode)
  (dired-sidebar-mode-hook . hide-cursor-mode)
  :bind
  (:map leader-map ("n" . dired-sidebar-toggle-sidebar)))
#+end_src

*** Subtree
Display subtrees in dired view.
#+begin_src emacs-lisp :tangle yes
(use-package dired-subtree
  :ensure t
  :init
  (setq dired-subtree-use-backgrounds nil)
  (setq dired-subtree-line-prefix "     "))
#+end_src

*** Stripes
Striped dired buffers.
#+begin_src emacs-lisp :tangle yes
(use-package stripe-buffer
  :ensure t
  :commands (stripe-buffer-mode)
  :functions (stripe-buffer-maybe-enable-for-dired)
  :hook
  (dired-after-readin-hook . stripe-buffer-maybe-enable-for-dired)
  :config
  (defun stripe-buffer-maybe-enable-for-dired ()
    (unless (derived-mode-p 'dired-sidebar-mode)
      (stripe-buffer-mode 1))))
#+end_src

*** Icons
#+begin_src emacs-lisp :tangle yes
(use-package all-the-icons-dired
  :ensure t
  :hook
  (dired-mode-hook . all-the-icons-dired-mode))
#+end_src

*** Git
#+begin_src emacs-lisp :tangle yes
(use-package dired-git-info
  :ensure t
  :bind
  (:map dired-mode-map (")" . dired-git-info-mode)))
#+end_src

** Isearch
#+begin_src emacs-lisp :tangle yes
(use-package isearch
  :init
  (setq isearch-lazy-count t))
#+end_src

** Occur
#+begin_src emacs-lisp :tangle yes
(use-package replace
  :bind
  (:map occur-mode-map ("C-c '" . occur-edit-mode)))

(use-package noccur
  :ensure t
  :bind
  (:map goto-map ("O" . noccur-project)))
#+end_src

** Packages
*** avy
Jump to ...
#+begin_src emacs-lisp :tangle yes
(use-package avy
  :ensure t
  :bind
  (:map goto-map ("C" . avy-goto-char-in-line)))
#+end_src

*** bookmark
Keep track of bookmarks
#+begin_src emacs-lisp :tangle yes
(use-package bookmark
  :init
  (setq bookmark-save-flag 1))
#+end_src

*** browse-url
URL handling.
#+begin_src emacs-lisp :tangle yes
(use-package browse-url
  :bind
  (:map goto-map
        ("u" . browse-url-at-point)
        ("U" . browse-url)))
#+end_src

*** deadgrep
#+begin_src emacs-lisp :tangle yes
(use-package deadgrep
  :ensure t
  :bind
  ((:map search-map ("G" . deadgrep))
   (:map deadgrep-mode-map ("C-c '" . deadgrep-edit-mode))
   (:map deadgrep-edit-mode-map ("C-c C-c" . deadgrep-mode))))
#+end_src

*** find-file
#+begin_src emacs-lisp :tangle yes
(use-package find-file
  :init
  (setq-default ff-quiet-mode t)
  (put 'ff-search-directories
       'safe-local-variable
       (lambda (x) (cl-every #'stringp x))))
#+end_src

*** goto-last-change
Move point through buffer-undo-list positions.
#+begin_src emacs-lisp :tangle yes
(use-package goto-chg
  :ensure t
  :bind
  ((:map goto-map ("SPC" . goto-last-change))
   (:repeat-map goto-chg-repeat-map
                ("SPC" . goto-last-change)
                ("[" . goto-last-change)
                ("]" . goto-last-change-reverse))))
#+end_src

*** link-hint
Hint mode for links.
#+begin_src emacs-lisp :tangle yes
(use-package link-hint
  :ensure t
  :bind
  (:map goto-map
        ("l" . link-hint-open-link)
        ("L" . link-hint-copy-link)))
#+end_src

*** recentf
Keep track of recently opened files.
#+begin_src emacs-lisp :tangle yes
(use-package recentf
  :hook
  (after-init-hook . init-recentf-enable)
  :init
  (setq recentf-exclude
        (list "/tmp/"                        ; Temp-files
              "/dev/shm"                     ; Potential secrets
              "/ssh:"                        ; Files over SSH
              "/nix/store"                   ; Files in Nix store
              "/TAGS$"                       ; Tag files
              "^/\\.git/.+$"                 ; Git contents
              "\\.?ido\\.last$"
              "\\.revive$"
              "^/var/folders/.+$"
              (concat "^" cache-dir ".+$")
              (concat "^" data-dir ".+$")))
  (setq recentf-filename-handlers '(abbreviate-file-name))
  (setq recentf-max-menu-items 0)
  (setq recentf-max-saved-items 300)
  (setq recentf-auto-cleanup 'never)
  :config
  (defun init-recentf-enable ()
    (quiet! (recentf-mode 1))))
#+end_src

*** saveplace
Keep track of last point place to resume editing in the same file.
#+begin_src emacs-lisp :tangle yes
(use-package saveplace
  :defer 2
  :config
  (save-place-mode 1))
#+end_src

** Project
Retrieve project list via ghq.
#+begin_src emacs-lisp :tangle yes
(use-package project
  :commands
  (project-read-project-list-from-ghq)
  :config
  (defun project-read-project-list-from-ghq ()
    "Initialize `project--list' using contents of command ghq output."
    (interactive)
    (with-temp-buffer
      (call-process "ghq" nil t nil "list" "--full-path")
      (goto-char (point-min))
      (while (not (eobp))
        (cl-pushnew
         (list (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
         project--list
         :test #'equal)
        (forward-line 1))))

  (advice-add 'project--read-project-list :after #'project-read-project-list-from-ghq))
#+end_src

* Development
Reduce scroll margin.
#+begin_src emacs-lisp :tangle yes
(defun prog-mode-scroll-margin-setup ()
  "Setup `scroll-margin' for `prog-mode'."
  (setq-local scroll-margin 3))
(add-hook 'prog-mode-hook #'prog-mode-scroll-margin-setup)
#+end_src

** Compilation
Kill compilation process before starting another and save all buffers on ~compile~. Also there is ~compile-on-save-mode~ that will compile when files are saved, compilation buffer will only be displayed on error.
#+begin_src emacs-lisp :tangle yes
(use-package compile
  :functions
  (compilation-find-buffer
   compilation-finish-handle-buffer-display
   recompile)
  :bind
  (:map local-leader-map
        ("a" . compile-on-save-mode)
        ("c" . recompile)
        ("C" . compile))
  :preface
  (define-minor-mode compile-on-save-mode
    "Minor mode to automatically call `recompile' whenever the
current buffer is saved. When there is ongoing compilation,
nothing happens."
    :lighter " CoS"
    (if compile-on-save-mode
        (progn  (make-local-variable 'after-save-hook)
                (add-hook 'after-save-hook 'compile-on-save-start nil t))
      (remove-hook 'after-save-hook 'compile-on-save-start t)))

  (make-variable-buffer-local 'compile-command)
  :init
  (setq compilation-always-kill t)
  (setq compilation-ask-about-save nil)
  (setq compilation-scroll-output t)

  (put 'compile-command 'safe-local-variable 'stringp)
  :config
  (defun compile-on-save-start ()
    (let ((compile-buffer (compilation-find-buffer)))
      (unless (get-buffer-process compile-buffer)
        (let ((display-buffer-alist '(("^*compilation*" . (display-buffer-no-window)))))
          (recompile)))))

  (defun compilation-finish-handle-buffer-display (buffer outstr)
    "Display failed compilation buffer or burry finished ones."
    (let ((compilation-window (get-buffer-window buffer)))
      (cond
       ;; If compilation failed and compilation buffer is not visible,
       ((and (not (string-match "finished" outstr))
             (not compilation-window))
        ;; display that buffer.
        (display-buffer (compilation-find-buffer)))
       ;; If compilation succeeded and compilation buffer is visible,
       ((and (string-match "finished" outstr)
             compilation-window)
        ;; bury that buffer.
        (with-selected-window compilation-window
          (bury-buffer))))))

  (cl-pushnew #'compilation-finish-handle-buffer-display compilation-finish-functions :test #'equal))
#+end_src

*** ANSI escape
#+begin_src emacs-lisp :tangle yes
(use-package ansi-color
  :hook
  (compilation-filter-hook . ansi-color-compilation-filter))
#+end_src

** Containers
*** Docker
#+begin_src emacs-lisp :tangle yes
(use-package docker
  :ensure t
  :bind
  (:map leader-map
        ("d" . docker)
        ("c" . docker-compose))
  :init
  (setq docker-command "podman"))
#+end_src

**** Files
Support for Docker related files.
#+begin_src emacs-lisp :tangle yes
(use-package dockerfile-ts-mode
  :mode
  "Dockerfile\\'"
  "\\.dockerfile\\'")
#+end_src

*** Kubernetes
#+begin_src emacs-lisp :tangle no
(use-package kubernetes :ensure t)
#+end_src

** Coverage
#+begin_src emacs-lisp :tangle yes
(use-package cov
  :ensure t
  :commands
  (cov-mode
   cov-mode-toggle)
  :bind
  (:map prog-mode-map ("C-. v" . cov-mode-toggle))
  :init
  (setq cov-coverage-mode t)
  :config
  (defun cov-mode-toggle ()
    "Turn on `cov-mode'."
    (interactive)
    (require 'cov)
    (if (bound-and-true-p cov-mode)
        (cov-mode 0)
      (unless cov-lcov-file-name
        (setq cov-lcov-file-name (expand-file-name "lcov.info" (project-root (project-current)))))
      (cov-mode 1))))
#+end_src

** Docs
*** DevDocs
Lookup documentation via DevDocs.
#+begin_src emacs-lisp :tangle yes
(use-package devdocs
  :ensure t
  :bind
  (:map goto-map ("K" . devdocs-lookup)))
#+end_src

** Folding
Code folding.
#+begin_src emacs-lisp :tangle yes
(use-package hideshow
  :functions
  (hs-fold-overlay-ellipsis)
  :hook
  (prog-mode-hook . hs-minor-mode)
  :bind
  (:map leader-map :prefix-map hs-prefix-map :prefix "z"
        ("c" . hs-hide-block)
        ("o" . hs-show-block)
        ("C" . hs-hide-all)
        ("O" . hs-show-all)
        ("l" . hs-hide-level)
        ("z" . hs-toggle-hiding)
        ("<tab>" . hs-toggle-hiding))
  :init
  (setq hs-hide-comments-when-hiding-all nil)
  (setq hs-allow-nesting t)
  (setq hs-set-up-overlay #'hs-fold-overlay-ellipsis)
  :config
  (defun hs-fold-overlay-ellipsis (ov)
    (when (eq 'code (overlay-get ov 'hs))
      (overlay-put
       ov 'display (propertize " … " 'face 'font-lock-comment-face)))))
#+end_src

** Formatting
*** apheleia
Reformat buffer without moving point.
#+begin_src emacs-lisp :tangle yes
(use-package apheleia
  :ensure t
  :hook
  (prog-mode-hook . apheleia-mode)
  :bind
  (:map local-leader-map ("f" . apheleia-format-buffer))
  :config
  (dolist (formatter '((nix . ("nix" "fmt" "--" "-"))
                       (rufo . ("rufo" "--simple-exit"))))
    (cl-pushnew formatter apheleia-formatters :test #'equal)))
#+end_src

** Jump to definition
Jump to definition is really useful and I prefer doing so without TAGS which is pretty
much the default for most modes. I am using the excellent package ~dumb-jump~ to jump via
grep tools e.g. (=grep=, =rx=, =ag=)

Don't ask about keeping current list of tags tables.
#+begin_src emacs-lisp :tangle yes
(use-package etags
  :init
  (setq tags-add-tables nil))
#+end_src

*** xref
#+begin_src emacs-lisp :tangle yes
(use-package xref
  :commands
  (xref-show-definitions-completing-read)
  :bind
  (:map goto-map ("o" . xref-find-definitions-other-window))
  :config
  (remove-hook 'xref-backend-functions #'etags--xref-backend))
#+end_src

*** dumb-jump
#+begin_src emacs-lisp :tangle yes
(use-package dumb-jump
  :ensure t
  :hook
  (xref-backend-functions . dumb-jump-xref-activate)
  :init
  (setq dumb-jump-default-project user-emacs-directory)
  (setq dumb-jump-selector 'completing-read))
#+end_src

** REPL
*** comint
#+begin_src emacs-lisp :tangle yes
(use-package comint
  :bind
  (:map comint-mode-map ("C-c C-k" . term-char-mode))
  :init
  (setq comint-use-prompt-regexp t))
#+end_src

*** repl-toggle
#+begin_src emacs-lisp :tangle yes
(use-package repl-toggle
  :ensure t
  :bind
  (:map local-leader-map ("r" . rtog/toggle-repl))
  :init
  (setq rtog/goto-buffer-fun 'pop-to-buffer)
  (setq rtog/mode-repl-alist
        '((emacs-lisp-mode . ielm)
          (org-mode . ielm))))
#+end_src

*** Persistent history in comint
#+begin_src emacs-lisp :tangle yes
(use-package comint
  :functions
  (comint-read-input-ring
   comint-write-input-ring
   comint-history-enable
   comint-history-write-on-exit)
  :config
  (defun comint-history-write-on-exit (process event)
    "Write `comint' history on exit.
Receives PROCESS and EVENT."
    (comint-write-input-ring)
    (let ((buf (process-buffer process)))
      (when (buffer-live-p buf)
        (with-current-buffer buf
          (insert (format "\nProcess %s %s" process event))))))

  (defun comint-history-enable ()
    "Enable `comint' history."
    (let ((process (get-buffer-process (current-buffer))))
      (when process
        (setq comint-input-ring-file-name
              (expand-file-name
               (format "comint-%s-history" (process-name process))
               cache-dir))
        (comint-read-input-ring)
        (set-process-sentinel process #'comint-history-write-on-exit)))))
#+end_src

** Syntax checker
Silence next/previous error, by default it produces a message every time.
#+begin_src emacs-lisp :tangle yes
(advice-add 'next-error :around #'quiet-function-advice)
(advice-add 'previous-error :around #'quiet-function-advice)
#+end_src

*** flymake
Connect flymake to =next-error-function= and add some navigation bindings. Disable the
legacy diagnostic functions as some of them have bugs and cause instability (mainly the
Haskell one).
#+begin_src emacs-lisp :tangle yes
(use-package flymake
  :hook
  (flymake-mode-hook . flymake-setup-next-error-function)
  :commands
  (flymake-show-diagnostic
   flymake-goto-next-error)
  :bind
  ((:map flymake-mode-map
         ("C-. !" . flymake-show-buffer-diagnostics)
         ("M-] E" . flymake-goto-next-error)
         ("M-[ E" . flymake-goto-prev-error))
   (:map flymake-diagnostics-buffer-mode-map
         ("n" . flymake-diagnostics-next-error)
         ("p" . flymake-diagnostics-prev-error)
         ("j" . flymake-diagnostics-next-error)
         ("k" . flymake-diagnostics-prev-error)
         ("TAB" . flymake-show-diagnostic))
   (:repeat-map flymake-mode-repeat-map
                ("e" . flymake-goto-next-error)
                ("E" . flymake-goto-prev-error)
                ("[" . flymake-goto-prev-error)
                ("]" . flymake-goto-next-error)))
  :config
  (defun flymake-setup-next-error-function ()
    (setq next-error-function 'flymake-next-error-compat))

  (defun flymake-next-error-compat (&optional n _)
    (flymake-goto-next-error n))

  (defun flymake-diagnostics-next-error ()
    (interactive)
    (forward-line)
    (when (eobp) (forward-line -1))
    (flymake-show-diagnostic (point)))

  (defun flymake-diagnostics-prev-error ()
    (interactive)
    (forward-line -1)
    (flymake-show-diagnostic (point))))

(use-package flymake-proc
  :config
  (setq flymake-proc-ignored-file-name-regexps '("\\.l?hs\\'"))
  (remove-hook 'flymake-diagnostic-functions 'flymake-proc-legacy-flymake))
#+end_src

** Version control
#+begin_src emacs-lisp :tangle yes
(setq vc-follow-symlinks t)
(setq vc-make-backup-files nil)
#+end_src

*** browse-at-remote
Open link to files in the web UI connected to a repository.
#+begin_src emacs-lisp :tangle yes
(use-package browse-at-remote
  :ensure t
  :bind
  (:map vc-prefix-map ("SPC" . browse-at-remote-kill)))
#+end_src

#+begin_src emacs-lisp :tangle yes
(use-package smerge-mode
  :bind
  (:repeat-map smerge-repeat-map
               ("[" . smerge-prev)
               ("]" . smerge-next)
               ("n" . smerge-next)
               ("p" . smerge-prev)

               ("RET" . smerge-keep-current)
               ("a" . smerge-keep-all)
               ("b" . smerge-keep-base)
               ("l" . smerge-keep-lower)
               ("u" . smerge-keep-upper)

               ("E" . smerge-ediff)
               ("r" . smerge-resolve)
               ("R" . smerge-refine))
  :init
  (setq smerge-command-prefix (kbd "C-x c"))
  (keymap-set toggle-map "m" '("Merging" . smerge-mode)))
#+end_src

*** diff
#+begin_src emacs-lisp :tangle yes
(use-package diff
  :init
  (setq diff-font-lock-prettify t))
#+end_src

*** ediff
- Split horizontally
- Use existing frame instead of creating a new one
- Add a third resolution option, copy both A and B to C
#+begin_src emacs-lisp :tangle yes
(use-package ediff
  :commands
  (ediff-copy-both-to-C)
  :preface
  (autoload 'ediff-copy-diff "ediff-util")
  (autoload 'ediff-get-region-contents "ediff-util")
  (autoload 'ediff-setup-windows-plain "ediff-wind")
  :hook
  (ediff-quit-hook . winner-undo)
  (ediff-keymap-setup-hook . init-ediff-keys)
  :init
  (setq ediff-diff-options "-w")
  (setq ediff-merge-split-window-function #'split-window-horizontally)
  (setq ediff-split-window-function #'split-window-horizontally)
  (setq ediff-window-setup-function #'ediff-setup-windows-plain)
  :config
  (defun ediff-copy-both-to-C ()
    "Copy change from both A and B to C."
    (interactive)
    (ediff-copy-diff
     ediff-current-difference nil 'C nil
     (concat
      (ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer)
      (ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer))))

  (defun init-ediff-keys ()
    "Setup keybindings for `ediff-mode'."
    (keymap-set ediff-mode-map "d" '("Copy both to C" . ediff-copy-both-to-C))
    (keymap-set ediff-mode-map "j" '("Next difference" . ediff-next-difference))
    (keymap-set ediff-mode-map "k" '("Previous difference" . ediff-previous-difference))))
#+end_src

*** diff-hl
Diff indicators in fringe
#+begin_src emacs-lisp :tangle yes
(use-package diff-hl
  :ensure t
  :hook
  (after-init-hook . global-diff-hl-mode)
  (after-init-hook . diff-hl-flydiff-mode)
  (dired-mode-hook . diff-hl-dired-mode)
  (magit-post-refresh-hook . diff-hl-magit-post-refresh)
  :init
  (setq diff-hl-ask-before-revert-hunk nil)
  (setq diff-hl-update-async t))
#+end_src

*** magit
Enhanced git related views and commands.
#+begin_src emacs-lisp :tangle yes
(use-package magit
  :ensure t
  :defer 2
  :functions
  (magit-commit-message-buffer
   magit-process-ask-create-pull-request)
  :preface
  (autoload 'magit-show-commit "magit-diff" nil t)
  :hook
  (git-commit-mode-hook . git-commit-set-fill-column)
  :bind
  ("C-x g" . magit)
  :init
  (setq magit-log-buffer-file-locked t)
  (setq magit-refs-show-commit-count 'all)
  (setq magit-save-repository-buffers 'dontask)
  (setq magit-process-prompt-functions #'magit-process-ask-create-pull-request)
  :config
  (defun git-commit-set-fill-column ()
    "Set `fill-column' for git commit."
    (setq fill-column 72))

  (defvar magit-process-create-pull-request-regexp
    "remote: Create pull request for.*\nremote: +\\(?1:[^ ]+\\)[^\n]*"
    "Regular expression detecting PR.")

  (defun magit-process-ask-create-pull-request (_process string)
    "Detect pull request STRING and ask to create PR."
    (when (string-match magit-process-create-pull-request-regexp string)
      (let ((url (match-string 1 string))
            (inhibit-message t))
        (if (y-or-n-p "Create PR? ")
            (browse-url (url-encode-url url))))))

  ;; Unset pager as it is not supported properly inside emacs.
  (setenv "GIT_PAGER" ""))
#+end_src

** Packages
*** envrc
Project-specific environment variables via =direnv=.
#+begin_src emacs-lisp :tangle yes
(use-package envrc
  :ensure t
  :if (executable-find "direnv")
  :defer 1
  :commands
  (envrc-global-mode)
  :bind
  (:map envrc-mode-map ("C-c e" . envrc-command-map))
  :config
  (envrc-global-mode 1))
#+end_src

*** eglot
Generic Language Server Protocol integration via ~eglot~.
#+begin_src emacs-lisp :tangle yes
(use-package eglot
  :defer 3
  :bind
  ((:map eglot-mode-map ("C-. C-." . eglot-code-actions))
   (:map eglot-mode-map :prefix-map eglot-prefix-map :prefix "C-. e"
         ("a" . eglot-code-actions)
         ("f" . eglot-format)
         ("r" . eglot-rename)
         ("q" . eglot-reconnect)
         ("Q" . eglot-shutdown)))
  :functions
  (eglot-completion-at-point)
  :init
  (setq eglot-autoshutdown t)
  (setq eglot-code-action-indications nil)
  (setq eglot-confirm-server-edits nil)
  (setq eglot-sync-connect nil)
  :config
  (cl-pushnew '((elixir-mode elixir-ts-mode heex-ts-mode) . ("elixir-ls"))
              eglot-server-programs
              :test #'equal)
  (cl-pushnew '((terraform-mode) . ("tofu-ls" "serve"))
              eglot-server-programs
              :test #'equal))
#+end_src

*** hl-todo
Highlight *TODO* inside comments and strings.
#+begin_src emacs-lisp :tangle yes
(use-package hl-todo
  :ensure t
  :hook
  (prog-mode-hook . hl-todo-mode)
  :bind
  (:repeat-map hl-todo-repeat-map
               ("t" . hl-todo-next)
               ("T" . hl-todo-previous)
               ("[" . hl-todo-previous)
               ("]" . hl-todo-next))
  :init
  (keymap-set next-map "t" '("Todo" . hl-todo-next))
  (keymap-set prev-map "t" '("Todo" . hl-todo-previous)))
#+end_src

*** idle-highlight
Highlight symbol at point on idle.
#+begin_src emacs-lisp :tangle yes
(use-package idle-highlight-mode
  :ensure t
  :hook
  (prog-mode-hook . idle-highlight-mode)
  :init
  (setq idle-highlight-exclude-point t))
#+end_src

*** imenu-anywhere
Jump to document locations in current buffer
#+begin_src emacs-lisp :tangle yes
(use-package imenu-anywhere
  :ensure t
  :bind
  (:map search-map
        ("i" . imenu)
        ("I" . imenu-anywhere)))
#+end_src

*** imenu-extra
Easily add document locations via regular expressions.
#+begin_src emacs-lisp :tangle yes
(use-package imenu-extra
  :ensure t
  :functions (imenu-extra-auto-setup))
#+end_src

*** imenu-list
Document locations in a sidebar.
#+begin_src emacs-lisp :tangle yes
(use-package imenu-list
  :ensure t
  :hook
  (imenu-list-major-mode-hook . hide-mode-line-mode)
  (imenu-list-major-mode-hook . hide-cursor-mode)
  :bind
  (:map leader-map ("i" . imenu-list-smart-toggle)))
#+end_src

*** ws-butler
Delete trailing white-space before save, but *only* for edited lines.
#+begin_src emacs-lisp :tangle yes
(use-package ws-butler
  :ensure t
  :commands
  (ws-butler-mode)
  :hook
  ((prog-mode-hook
    text-mode-hook) . ws-butler-mode)
  :init
  (setq ws-butler-convert-leading-tabs-or-spaces t))
#+end_src

** Appearance
*** Highlight delimiters
Visually separate delimiter pairs.
#+begin_src emacs-lisp :tangle no
(use-package rainbow-delimiters
  :ensure t
  :hook
  ((clojure-mode-hook
    emacs-lisp-mode-hook
    ielm-mode-hook
    lisp-mode-hook
    racket-mode-hook) . rainbow-delimiters-mode)
  :init
  (setq rainbow-delimiters-max-face-count 3))
#+end_src

*** Highlight identifiers
Highlight source code identifiers based on their name.
#+begin_src emacs-lisp :tangle no
(use-package color-identifiers-mode :ensure t)
#+end_src

*** Ligatures
#+begin_src emacs-lisp :tangle yes
(use-package ligature
  :ensure t
  :commands
  (ligature-set-ligatures)
  :hook
  (prog-mode-hook . ligature-mode)
  :config
  (ligature-set-ligatures
   'prog-mode
   '("-<<" "-<" "-<-" "<--" "<---" "<<-" "<-" "->" "->>" "-->" "--->" "->-" ">-" ">>-" "<->" "<-->" "<--->" "<---->" "<!--"
     "=<<" "=<" "=<=" "<==" "<===" "<<=" "<=" "=>" "=>>" "==>" "===>" "=>=" ">=" ">>=" "<=>" "<==>" "<===>" "<====>" "<!---"
     "<------" "------>" "<=====>" "<~~" "<~" "~>" "~~>" "::" ":::" "\\/" "/\\" "==" "!=" "/=" "~=" "<>" "===" "!==" "=/=" "=!="
     ":=" ":-" ":+" "<*" "<*>" "*>" "<|" "<|>" "|>" "<." "<.>" ".>" "+:" "-:" "=:" "<******>" "(*" "*)" "++" "+++" "|-" "-|"
     "&&" "||")))

(use-package prog-mode
  :init
  (setq prettify-symbols-unprettify-at-point t))
#+end_src

* Major modes
** emacs-lisp
#+begin_src emacs-lisp :tangle yes
(use-package elisp-mode
  :hook
  (emacs-lisp-mode-hook . flymake-mode)
  (emacs-lisp-mode-hook . outline-minor-mode)
  :bind
  (:map emacs-lisp-mode-map
        ("C-. c" . emacs-lisp-byte-compile)
        ("C-. C" . emacs-lisp-byte-compile-and-load)
        ("C-. d" . checkdoc)
        ("C-. l" . emacs-lisp-load-buffer-file)
        ("C-. L" . eval-buffer)
        ("C-. p" . package-lint-current-buffer)
        ("C-. t" . ert))
  :config
  (defun emacs-lisp-load-buffer-file ()
    "Load the Lisp file associated with current buffer."
    (interactive)
    (load-file (buffer-file-name))))

(custom-set-variables
 '(ad-redefinition-action 'accept)
 '(apropos-do-all t)
 '(enable-local-eval 'maybe)
 '(enable-local-variables :safe))
#+end_src

Specific safe local code can be specified via:
- ~safe-local-variable-values~
- ~safe-local-eval-forms~
- ~safe-local-eval-function~

*** Packages
**** package-lint-flymake
#+begin_src emacs-lisp :tangle yes
(use-package package-lint-flymake
  :ensure t
  :commands (package-lint-flymake-setup)
  :preface
  (autoload 'package-lint--provided-feature "package-lint")
  :hook
  (emacs-lisp-mode-hook . package-lint-flymake-setup-when-package)
  :config
  (defun package-lint-flymake-setup-when-package ()
    "Enable `package-lint-flymake' when buffer seems to be a package."
    (when (package-lint--provided-feature)
      (package-lint-flymake-setup))))
#+end_src

*** Appearance
**** Regular expression escapes
Improve readability of escape characters in regular expressions.
#+begin_src emacs-lisp :tangle yes
(use-package easy-escape
  :ensure t
  :hook
  (emacs-lisp-mode-hook . easy-escape-minor-mode))
#+end_src

** erc
#+begin_src emacs-lisp :tangle yes
(use-package erc
  :init
  (setq erc-hide-list '("JOIN" "PART" "QUIT"))
  (setq erc-prompt-for-password nil))
#+end_src

** org
#+begin_src emacs-lisp :tangle yes
(use-package org
  :defer 5
  :preface
  (autoload 'org-get-outline-path "org-refile" nil t)
  :hook
  (org-mode-hook . auto-fill-mode)
  (org-mode-hook . org-mode-init)
  :bind
  (("C-c a" . org-agenda)
   (:map org-mode-map
         ("C-'" . nil)
         ("C-," . nil)))
  :init
  (setq org-agenda-files
        '("~/org/Personal.org" "~/org/Work.org"))
  (setq org-insert-heading-respect-content t)
  (setq org-log-done 'time)
  (setq org-modules '(ol-info))
  (setq org-special-ctrl-a/e t)

  (setq org-confirm-babel-evaluate nil)
  (setq org-src-content-indentation 0)
  (setq org-src-preserve-indentation nil)
  (setq org-src-tab-acts-natively t)
  (setq org-src-window-setup 'current-window)

  (setq org-startup-truncated nil)

  (setq org-auto-align-tags nil)
  (setq org-tags-column 0)
  (setq org-tag-alist
        '(("@work"  . ?w)
          ("@home"  . ?h)
          ("laptop" . ?l)))

  (setq org-imenu-depth 3)
  :config
  (defun org-mode-init ()
    (setq tab-width 8))

  (dolist (lang '("emacs-lisp" "elisp"))
    (cl-pushnew (cons lang 'lisp-interaction) org-src-lang-modes :test #'equal)))

(use-package ob-plantuml
  :init
  (setq org-plantuml-exec-mode 'plantuml))
#+end_src

*** Packages
**** dslide
Presentation mode.
#+begin_src emacs-lisp :tangle yes
(use-package dslide
  :ensure t
  :functions
  (dslide-stop-restore-modes)
  :hook
  (dslide-start-hook . hide-cursor-mode)
  (dslide-start-hook . global-hide-fringes-mode)
  (dslide-start-hook . hide-mode-line-mode)
  (dslide-stop-hook . dslide-stop-restore-modes)
  :bind
  (:map org-mode-map ("C-. p" . dslide-deck-start))
  :init
  (setq dslide-header nil)
  :config
  (defun dslide-stop-restore-modes ()
    (global-hide-fringes-mode 0)))
#+end_src

**** org-contrib
Unmaintained add-ons for org-mode, in this configuration =org-eldoc= is used.
#+begin_src emacs-lisp :tangle yes
(use-package org-contrib :ensure t)
#+end_src

**** org-cliplink
Paste links from clipboard and automatically fetch title.
#+begin_src emacs-lisp :tangle yes
(use-package org-cliplink
  :ensure t
  :bind
  (:map org-mode-map ("C-. l" . org-cliplink)))
#+end_src

**** org-eldoc
Eldoc support, showing path to current section.
#+begin_src emacs-lisp :tangle yes
(use-package org-eldoc
  :after eldoc
  :functions
  (org-eldoc-get-breadcrumb-no-properties)
  :hook
  (org-mode-hook . org-eldoc-load)
  :config
  (defun org-eldoc-get-breadcrumb-no-properties (string)
    "Remove properties from STRING."
    (when string (substring-no-properties string)))

  (advice-add 'org-eldoc-get-breadcrumb :filter-return #'org-eldoc-get-breadcrumb-no-properties))
#+end_src

**** org-limit-image-size
#+begin_src emacs-lisp :tangle yes
(use-package org-limit-image-size
  :ensure t
  :after org
  :commands
  (org-limit-image-size-activate
   org-limit-image-size-deactivate)
  :init
  (org-limit-image-size-activate)
  (setq org-limit-image-size '(0.48 . 0.8)))
#+end_src

**** org-noter
Annotate documents with ~org-mode~.
#+begin_src emacs-lisp :tangle yes
(use-package org-noter :ensure t
  :bind
  (:map leader-map ("o" . org-noter)))
#+end_src

**** ob-http
#+begin_src emacs-lisp :tangle yes
(use-package ob-http :ensure t)
#+end_src

*** Appearance
This sections makes ~org-mode~ look more beautiful and appealing.

#+begin_src emacs-lisp :tangle yes
(use-package org
  :hook
  (org-babel-after-execute-hook . org-link-preview-refresh)
  :init
  (setq org-fold-catch-invisible-edits 'show-and-error)
  (setq org-fontify-quote-and-verse-blocks t)
  (setq org-hide-emphasis-markers t)
  (setq org-pretty-entities t)
  (setq org-src-fontify-natively t)
  (setq org-startup-with-inline-images t))

(use-package org-modern
  :ensure t
  :hook
  (org-mode-hook . org-modern-mode)
  :init
  (setq org-modern-hide-stars t)
  (setq org-modern-table nil))

(use-package org-appear
  :ensure t
  :hook
  (org-mode-hook . org-appear-mode))
#+end_src

**** Emphasis
Allow more newlines (1 to 4) for emphasized text, useful when filling long text.
#+begin_src emacs-lisp :tangle yes
(use-package org
  :preface
  (autoload 'org-set-emph-re "org")
  :config
  (setcar (nthcdr 4 org-emphasis-regexp-components) 4)
  (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components))
#+end_src

**** Headings
#+begin_src emacs-lisp :tangle yes
(use-package org
  :init
  (setq org-ellipsis " ┅ ")
  (setq org-fontify-whole-heading-line t)
  (setq org-fontify-todo-headline t)
  (setq org-fontify-done-headline t))
#+end_src

*** Export
**** epub
#+begin_src emacs-lisp :tangle yes
(use-package ox-epub :ensure t)
#+end_src

**** HTML
#+begin_src emacs-lisp :tangle yes
(use-package ox-html
  :init
  (setq org-html-postamble nil)
  (setq org-html-validation-link nil))
#+end_src

**** LaTeX
Disable link colors.
#+begin_src emacs-lisp :tangle yes
(use-package ox-latex
  :init
  (setq org-latex-hyperref-template
        (mapconcat
         'identity
         '("\\hypersetup{"
           "pdfauthor={%a},"
           "pdftitle={%t},"
           "pdfkeywords={%k},"
           "pdfsubject={%d},"
           "pdfcreator={%c},"
           "pdflang={%L},"
           "pdfborder=0 0 0}")
         "\n")))
#+end_src

Add links in footnotes.
#+begin_src emacs-lisp :tangle yes
(autoload 'org-export-derived-backend-p "ox")

(defvar org-export-latex-add-link-footnotes t
  "If non-nil links will be added as footnotes if exported to latex.")

(defun org-export-latex-link-footnote (text backend _info)
  "Create a footnote for each link to retain this information for print.
If there is a URL and the export BACKEND is latex, then extract
URL into footnote from TEXT."
  (when (and org-export-latex-add-link-footnotes
             (org-export-derived-backend-p backend 'latex)
             (string-match "\\\\href{\\(.*\\)}{\\(.*\\)}" text))
    (when (cl-some (lambda (type)
                     (string-prefix-p type (match-string 1 text)))
                   '("http" "https" "ftp" "mailto" "doi"))
      (format "%s \\footnote{\\url{%s}} " text (match-string 1 text)))))

(with-eval-after-load 'ox
  (cl-pushnew #'org-export-latex-link-footnote org-export-filter-link-functions :test #'equal))
#+end_src

** outline
Display outlines in text like files or use it in conjunction with other major modes via ~outline-minor-mode~.
*** Packages
**** outline-minor-faces
Add faces to =outline-minor-mode= in order to make the headings stand out.
#+begin_src emacs-lisp :tangle yes
(use-package outline-minor-faces
  :ensure t
  :after outline
  :hook
  (outline-minor-mode-hook . outline-minor-faces-mode))
#+end_src

** clojure
#+begin_src emacs-lisp :tangle yes
(use-package clojure-mode
  :ensure t
  :mode
  "\\.\\(clj\\|dtm\\|edn\\)\\'"
  "\\(?:build\\|profile\\)\\.boot\\'"
  ("\\.cljc\\'" . clojurec-mode)
  ("\\.cljs\\'" . clojurescript-mode)
  :config
  (cl-pushnew '("src/\\([^/]+\\)\\.clj\\'" "test/\\1_test.clj") find-sibling-rules :test #'equal)
  (cl-pushnew '("test/\\([^/]+\\)_test\\.clj\\'" "src/\\1.clj") find-sibling-rules :test #'equal))
#+end_src

*** Packages
**** cider
#+begin_src emacs-lisp :tangle yes
(use-package cider
  :ensure t
  :commands
  (cider-current-repl
   cider-jack-in-clj)
  :hook
  (clojure-mode-hook . cider-mode)
  (cider-mode-hook . cider-auto-test-mode)
  :bind
  ((:map cider-mode-map
         ("C-. R" . cider-jack-in)
         ("C-. c" . cider-ns-refresh)
         ("C-. l" . cider-load-buffer)
         ("C-. t a" . cider-test-run-project-tests)
         ("C-. t n" . cider-test-run-ns-tests)
         ("C-. t t" . cider-test-run-test)
         ("C-. t w" . cider-auto-test-mode)))
  :init
  (setq cider-prompt-for-symbol nil)
  (setq cider-repl-display-help-banner nil)
  (setq cider-repl-history-file (concat data-dir "cider-history"))

  (with-eval-after-load 'popper
    (cl-pushnew 'cider-test-report-mode popper-reference-buffers :test #'equal))
  :config
  (defun clojure-repl ()
    "Open a Clojure REPL."
    (interactive)
    (if-let* ((repl-buffer (cider-current-repl)))
        (pop-to-buffer repl-buffer)
      (cider-jack-in-clj nil)))

  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(clojure-mode . clojure-repl) rtog/mode-repl-alist :test #'equal)))
#+end_src

** cobol
#+begin_src emacs-lisp :tangle yes
(use-package cobol-mode
  :ensure t
  :mode "\\.\\(cob\\|cbl\\|cpy\\)\\'")
#+end_src

** crystal
#+begin_src emacs-lisp :tangle yes
(use-package crystal-mode
  :ensure t
  :mode "\\(?:\\.cr\\)\\'"
  :bind
  ((:map crystal-mode-map
         ("C-. t a" . crystal-spec-all)
         ("C-. t f" . crystal-spec-buffer)
         ("C-. t t" . crystal-spec-line))))
#+end_src

*** Packages
**** inf-crystal
#+begin_src emacs-lisp :tangle yes
(use-package inf-crystal
  :ensure t
  :commands
  (inf-crystal)
  :hook
  (crystal-mode-hook . inf-crystal-minor-mode)
  :config
  (defun crystal-repl ()
    "Open a Crystal REPL."
    (inf-crystal inf-crystal-interpreter))

  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(crystal-mode . crystal-repl) rtog/mode-repl-alist :test #'equal)))
#+end_src

** csv
#+begin_src emacs-lisp :tangle no
(use-package csv-mode
  :ensure t
  :mode
  "\\.[Cc][Ss][Vv]\\'"
  ("\\.tsv\\'" . tsv-mode)
  :init
  (setq csv-separators '("," "\t" ";")))
#+end_src

** d2
#+begin_src emacs-lisp :tangle yes
(use-package d2-mode
  :ensure t
  :mode "\\.d2\\'"
  :hook
  (d2-mode-hook . d2-mode-set-compile-command)
  (d2-mode-hook . compile-on-save-mode)
  :init
  (setq d2-flags '("--layout" "elk" "--sketch" "--theme" "1"))
  :config
  (defun d2-mode-set-compile-command ()
    "Configure compile command for d2-mode."
    (set (make-local-variable 'compile-command)
         (mapconcat #'shell-quote-argument (append (list d2-location buffer-file-name) d2-flags)
                    " "))))
#+end_src

** elixir
#+begin_src emacs-lisp :tangle yes
(use-package elixir-ts-mode
  :mode
  "\\.elixir\\'"
  "\\.ex\\'"
  "\\.exs\\'"
  :hook
  (elixir-ts-mode-hook . eglot-ensure)
  :init
  (cl-pushnew '("lib/\\([^/]+\\)\\.ex\\'" "test/\\1_test.exs") find-sibling-rules :test #'equal)
  (cl-pushnew '("test/\\([^/]+\\)_test\\.exs\\'" "lib/\\1.ex") find-sibling-rules :test #'equal))

(cl-pushnew '(elixir-mode . elixir-ts-mode) major-mode-remap-alist :test #'equal)
#+end_src

** elm
Elm and elm-mode seems unmaintained at the moment, so I disable this.
#+begin_src emacs-lisp :tangle no
(use-package elm-mode
  :ensure t
  :mode "\\.elm\\'"
  :bind
  (:map elm-mode-map ("C-. t" . elm-test-project))
  :init
  (setq elm-format-on-save t)
  (setq elm-package-json "elm.json")
  (setq elm-tags-exclude-elm-stuff nil)
  (setq elm-tags-on-save t)
  :config
  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(elm-mode . elm-repl-load) rtog/mode-repl-alist :test #'equal)))
#+end_src

** epub
*** Packages
**** nov
#+begin_src emacs-lisp :tangle yes
(use-package nov
  :ensure t
  :mode
  ("\\.epub\\'" . nov-mode)
  :hook
  (nov-mode-hook . init-nov-delayed-render)
  (nov-mode-hook . hide-fringes-mode)
  :init
  (setq nov-save-place-file (concat data-dir "nov-places"))
  :config
  (defun init-nov-delayed-render ()
    (run-with-idle-timer 0.2 nil 'nov-render-document)))
#+end_src

** erlang
#+begin_src emacs-lisp :tangle yes
(use-package erlang
  :ensure t
  :mode
  ("\\.\\(e\\|h\\|x\\|y\\)rl$" . erlang-mode)
  ("\\.escript" . erlang-mode)
  ("\\.app\\.src$" . erlang-mode)
  ("/ebin/.+\\.app" . erlang-mode)
  :hook
  (erlang-mode-hook . init-erlang-eunit)
  (erlang-mode-hook . init-erlang-flymake)
  :bind
  (:map erlang-mode-map ("M-g k" . erlang-man-function))
  :init
  (cl-pushnew '("src/\\([^/]+\\)\\.erl\\'" "test/\\1_tests.erl") find-sibling-rules :test #'equal)
  (cl-pushnew '("test/\\([^/]+\\)_tests\\.erl\\'" "src/\\1.erl") find-sibling-rules :test #'equal)
  :config
  (defun init-erlang-eunit ()
    "Setup EUnit support for `erlang-mode'."
    (require 'erlang-eunit))

  (defun init-erlang-flymake ()
    "Setup `flymake' support for `erlang-mode'."
    (require 'erlang-flymake)
    (flymake-mode 1))

  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(erlang-mode . erlang-shell-display) rtog/mode-repl-alist :test #'equal)))

(use-package erlang-eunit
  :bind
  (:map erlang-mode-map
        ("C-. t t" . erlang-eunit-compile-and-run-current-test)
        ("C-. t m" . erlang-eunit-compile-and-run-module-tests)))
#+end_src

** git
*** Packages
**** git-modes
#+begin_src emacs-lisp :tangle yes
(use-package git-modes
  :ensure t
  :mode
  ("/\\.dockerignore\\'" . gitignore-mode))
#+end_src

** go
#+begin_src emacs-lisp :tangle yes
(use-package go-ts-mode
  :mode
  "\\.go\\'"
  ("go\\.mod\\'" . go-mod-ts-mode)
  :hook
  (go-ts-mode-hook . eglot-ensure)
  (go-ts-mode-hook . go-ts-mode-set-devdocs)
  :preface
  (define-repl go-repl "Go REPL" "gore")
  :config
  (defun go-ts-mode-set-devdocs ()
    (setq-local devdocs-current-docs '("go")))

  (cl-pushnew '(go-mode . go-ts-mode) major-mode-remap-alist :test #'equal)

  (with-eval-after-load 'apheleia
    (cl-pushnew '(go-ts-mode . goimports) apheleia-mode-alist :test #'equal))

  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(go-ts-mode . go-repl) rtog/mode-repl-alist :test #'equal))

  (cl-pushnew '("\\([^/]+\\)\\.go\\'" "\\1_test.go") find-sibling-rules :test #'equal)
  (cl-pushnew '("\\([^/]+\\)_test\\.go\\'" "\\1.go") find-sibling-rules :test #'equal))
#+end_src

*** Packages
**** gotest
#+begin_src emacs-lisp :tangle yes
(use-package gotest
  :ensure t
  :bind
  (:map go-ts-mode-map
        ("C-. l" . go-run)
        ("C-. t a" . go-test-current-project)
        ("C-. t f" . go-test-current-file)
        ("C-. t t" . go-test-current-test)))
#+end_src

** groovy
#+begin_src emacs-lisp :tangle yes
(use-package groovy-mode
  :ensure t
  :mode
  "\\.g\\(?:ant\\|roovy\\|radle\\)\\'"
  "Jenkinsfile")
#+end_src

** fish
#+begin_src emacs-lisp :tangle yes
(use-package fish-mode
  :ensure t
  :mode
  "\\.fish\\'"
  "/fish_funced\\..*\\'")
#+end_src

** haskell
#+begin_src emacs-lisp :tangle yes
(use-package haskell-mode
  :ensure t
  :preface
  (eval-when-compile
    (require 'haskell-commands))
  :mode
  "\\.[gh]s\\'"
  "\\.hsig\\'"
  "\\.hsc\\'"
  ("\\.cabal\\'\\|/cabal\\.project\\|/\\.cabal/config\\'" . haskell-cabal-mode)
  ("\\.l[gh]s\\'" . haskell-literate-mode)
  :hook
  (haskell-mode-hook . eglot-ensure)
  :preface
  (autoload 'interactive-haskell-mode-map "haskell" nil t)
  :bind
  (:map haskell-mode-map
        ("C-. r" . haskell-interactive-switch)
        ("C-. R" . haskell-session-change-target))
  :init
  (setq haskell-font-lock-symbols t)
  (setq haskell-process-auto-import-loaded-modules t)
  (setq haskell-process-log t)
  (setq haskell-process-show-debug-tips nil)
  (setq haskell-process-use-presentation-mode t)
  (setq haskell-stylish-on-save t)
  (setq haskell-mode-stylish-haskell-path "brittany")

  ;; Allow configuring project local cabal repl commands.
  (put 'haskell-process-args-cabal-repl
       'safe-local-variable
       (lambda (x) (cl-every #'stringp x))))

(use-package haskell
  :hook
  (haskell-mode-hook . interactive-haskell-mode)
  :bind
  (:map interactive-haskell-mode-map ("M-." . nil)))
#+end_src

** java
#+begin_src emacs-lisp :tangle yes
(use-package java-ts-mode
  :preface
  (cl-pushnew '(java-mode . java-ts-mode) major-mode-remap-alist :test #'equal)
  :hook
  (java-ts-mode-hook . eglot-ensure))
#+end_src

*** Packages
**** gradle-mode
#+begin_src emacs-lisp :tangle yes
(use-package gradle-mode
  :ensure t
  :hook
  ((java-mode-hook java-ts-mode-hook kotlin-mode-hook) . gradle-mode)
  :bind
  (:map gradle-mode-map ("C-. t" . gradle-test))
  :init
  (setq gradle-executable-path "gradle"))
#+end_src

** js
#+begin_src emacs-lisp :tangle yes
(use-package js
  :mode
  ("\\.js[mx]?\\'" . js-ts-mode)
  ("\\.har\\'" . js-ts-mode)
  :hook
  (js-ts-mode-hook . eglot-ensure)
  :init
  (cl-pushnew '("\\([^/]+\\)\\.js\\'" "\\1.spec.js") find-sibling-rules :test #'equal)
  (cl-pushnew '("\\([^/]+\\).spec\\.js\\'" "\\1.js") find-sibling-rules :test #'equal))

(cl-pushnew '(js-mode . js-ts-mode) major-mode-remap-alist :test #'equal)
#+end_src

** json
#+begin_src emacs-lisp :tangle yes
(use-package json-ts-mode
  :mode
  "\\(?:\\(?:\\.json\\|\\.jsonld\\|\\.babelrc\\|\\.bowerrc\\|composer\\.lock\\)\\'\\)")

(cl-pushnew '(json-mode . json-ts-mode) major-mode-remap-alist :test #'equal)
#+end_src

*** Packages
**** json-navigator
#+begin_src emacs-lisp :tangle yes
(use-package json-navigator
  :ensure t
  :bind
  (:map json-ts-mode-map ("C-. n" . json-navigator-navigate-region)))
#+end_src

** kotlin
#+begin_src emacs-lisp :tangle yes
(use-package kotlin-mode :ensure t)
#+end_src

** lisp
#+begin_src emacs-lisp :tangle yes
(use-package inf-lisp
  :init
  (setq inferior-lisp-program "sbcl"))
#+end_src

*** Packages
**** sly
#+begin_src emacs-lisp :tangle yes
(use-package sly
  :ensure t
  :bind
  (:map sly-mode-map
        ("M-g k" . sly-describe-symbol)
        ("C-. o" . sly)))

(use-package sly-quicklisp :ensure t)
#+end_src

** lua
#+begin_src emacs-lisp :tangle yes
(use-package lua-ts-mode
  :mode "\\.lua\\'"
  :config
  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(lua-ts-mode . lua-ts-inferior-lua) rtog/mode-repl-alist :test #'equal)))
#+end_src

** markdown
#+begin_src emacs-lisp :tangle yes
(use-package markdown-mode
  :ensure t
  :mode
  "\\.markdown\\'"
  "\\.md\\'"
  :hook
  (markdown-mode-hook . markdown-display-inline-images)
  :bind
  (:map markdown-mode-map
        ("C-. i" . markdown-toggle-inline-images)
        ("C-. v" . markdown-toggle-markup-hiding))
  :init
  (setq markdown-enable-wiki-links t)
  (setq markdown-fontify-code-blocks-natively t)
  (setq markdown-header-scaling t)
  (setq markdown-hide-markup t)
  (setq markdown-italic-underscore t)
  (setq markdown-blockquote-display-char '("┃" ">"))
  (setq markdown-list-item-bullets '("⏺" "▪"))
  (setq markdown-make-gfm-checkboxes-buttons t)
  (setq markdown-max-image-size '(1024 . 1024)))
#+end_src

*** Packages
**** edit-indirect
Edit regions in separate buffers.
#+begin_src emacs-lisp :tangle yes
(use-package edit-indirect :ensure t)
#+end_src

**** markdown-toc
Generate Table of Contents.
#+begin_src emacs-lisp :tangle yes
(use-package markdown-toc :ensure t)
#+end_src

*** Appearance
**** Variable pitch
Use variable-pitch font but still make sure everything aligns.
#+begin_src emacs-lisp :tangle yes
(font-lock-add-keywords
 'markdown-mode
 '(("^[[:space:]-*+>]+" 0 'fixed-pitch append))
 'append)
#+end_src

**** Bullet lists
Pretty check-boxes
#+begin_src emacs-lisp :tangle yes
(font-lock-add-keywords
 'markdown-mode
 '(("^ *[-*+] \\[\\([Xx]\\)\\] "
    (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "✕"))))))
#+end_src

** mermaid
#+begin_src emacs-lisp :tangle no
(use-package mermaid-mode
  :ensure t
  :mode "\\.\\(mmd|mermaid\\)\\'"
  :hook
  (mermaid-mode-hook . mermaid-mode-set-compile-command)
  (mermaid-mode-hook . compile-on-save-mode)
  :init
  (setq mermaid-flags "--backgroundColor transparent")
  :config
  (defun mermaid-mode-set-compile-command ()
    "Configure compile command for mermaid-mode."
    (set (make-local-variable 'compile-command)
         (concat (mapconcat #'shell-quote-argument
                            (list
                             mermaid-mmdc-location
                             "-i" buffer-file-name
                             "-o" (concat (file-name-sans-extension buffer-file-name) mermaid-output-format)) " ")
                 " "
                 mermaid-flags))))
#+end_src

** message
#+begin_src emacs-lisp :tangle yes
(use-package message
  :init
  (setq message-expand-name-standard-ui t))
#+end_src

** nginx
#+begin_src emacs-lisp :tangle no
(use-package nginx-mode
  :ensure t
  :mode
  "/nginx/.+\\.conf\\'"
  "nginx\\.conf\\'")
#+end_src

** nix
#+begin_src emacs-lisp :tangle yes
(use-package nix-ts-mode
  :ensure t
  :mode "\\.nix\\'"
  :hook
  (nix-ts-mode-hook . eglot-ensure)
  (nix-ts-mode-hook . nix-ts-mode-set-devdocs)
  :init
  (with-eval-after-load 'apheleia
    (cl-pushnew '(nix-ts-mode . nix) apheleia-mode-alist :test #'equal))
  (with-eval-after-load 'grugru
    (grugru-define-on-major-mode 'nix-ts-mode 'symbol '("true" "false")))
  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(nix-ts-mode . nix-repl) rtog/mode-repl-alist :test #'equal))
  :config
  (defun nix-ts-mode-set-devdocs ()
    (setq-local devdocs-current-docs '("nix"))))

(use-package nix-mode
  :ensure t
  :commands
  (nix-repl)
  :bind
  (:map leader-map ("f" . nix-flake)))
#+end_src

#+begin_src emacs-lisp :tangle yes
(use-package nushell-ts-mode
  :ensure t
  :mode "\\.nu\\'")
#+end_src

** nxml
#+begin_src emacs-lisp :tangle yes
(use-package nxml-mode
  :mode "\\.plist\\'"
  :init
  (setq nxml-slash-auto-complete-flag t))
#+end_src

** ocaml
#+begin_src emacs-lisp :tangle yes
(use-package caml :ensure t)

(use-package tuareg
  :ensure t
  :bind
  ((:map tuareg-mode-map
         ("C-x C-e" . tuareg-eval-phrase)
         ("C-. r" . run-ocaml))))
#+end_src

** pdf
*** Packages
**** pdf-tools
#+begin_src emacs-lisp :tangle yes
(use-package pdf-tools
  :ensure t
  :mode ("\\.pdf\\'" . pdf-view-mode))
#+end_src

** plantuml
#+begin_src emacs-lisp :tangle no
(use-package plantuml-mode
  :ensure t
  :mode "\\.\\(plantuml\\|pum\\|plu\\)\\'"
  :init
  (setq plantuml-default-exec-mode 'executable))
#+end_src

** python
#+begin_src emacs-lisp :tangle yes
(use-package python
  :hook
  (python-ts-mode-hook . eglot-ensure)
  (python-ts-mode-hook . indent-bars-mode)
  :bind
  (:map python-ts-mode-map ("C-. r" . run-python))
  :init
  (cl-pushnew '("\\([^/]+\\)\\.py\\'" "\\1_test.py") find-sibling-rules :test #'equal)
  (cl-pushnew '("\\([^/]+\\)_test\\.py\\'" "\\1.py") find-sibling-rules :test #'equal)
  (cl-pushnew '("src/\\(\\(?:[s][r][^c]\\|[^s][^r][^c]\\|$\\)./\\)+\\([^/]+\\)\\.py\\'" "tests/\\1test_\\2.py") find-sibling-rules :test #'equal)
  (cl-pushnew '("tests/\\(.+/\\)test_\\([^/]+\\)\\.py\\'" "src/\\1\\2.py") find-sibling-rules :test #'equal)
  :config
  (put 'python-shell-interpreter 'safe-local-variable #'(lambda (x) (member x '("python" "ipython")))))

(cl-pushnew '(python-mode . python-ts-mode) major-mode-remap-alist :test #'equal)
#+end_src

*** Packages
**** python-test
#+begin_src emacs-lisp :tangle yes
(use-package python-test
  :ensure t
  :bind
  (:map python-ts-mode-map
        ("C-. t a" . python-test-project)
        ("C-. t f" . python-test-file)
        ("C-. t t" . python-test-function))
  :init
  (setq python-test-backend 'pytest)
  :config
  (setq python-test-project-root-files
        (append '("README.md") python-test-project-root-files)))
#+end_src

** racket
#+begin_src emacs-lisp :tangle yes
(use-package racket-mode
  :ensure t
  :mode "\\.rkt[dl]?\\'"
  :bind
  (:map racket-mode-map
        ("M-g k" . racket-doc)
        ("C-. t" . racket-test))
  :config
  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(racket-mode . racket-repl) rtog/mode-repl-alist :test #'equal)))
#+end_src

*** Packages
**** flymake-racket
#+begin_src emacs-lisp :tangle yes
(use-package flymake-racket
  :ensure t
  :hook
  (racket-mode-hook . flymake-racket-add-hook))
#+end_src

** rego
Disabled due to currently not working with rego.
#+begin_src emacs-lisp :tangle no
(use-package rego-mode :ensure t)
#+end_src

** rst
#+begin_src emacs-lisp :tangle yes
(use-package rst
  :hook
  (rst-mode-hook . auto-fill-mode))
#+end_src

*** Appearance
**** Variable pitch
Use variable-pitch font but still make sure everything aligns.
#+begin_src emacs-lisp :tangle yes
(font-lock-add-keywords
 'rst-mode
 '(("^[[:space:]-*+]+\\(\\[.\\]\\)?" 0 'fixed-pitch append))
 'append)
#+end_src

**** Bullet lists
Pretty check-boxes as well as bullet lists.
#+begin_src emacs-lisp :tangle yes
(font-lock-add-keywords
 'rst-mode
 '(("^ *\\([-*+]\\) "
    (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "⏺"))))
   ("^ *[-*+] \\[\\([Xx]\\)\\] "
    (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "✕"))))))
#+end_src

** ruby
#+begin_src emacs-lisp :tangle yes
(use-package ruby-ts-mode
  :mode
  "\\.\\(rbw?\\|ru\\|rake\\|thor\\|jbuilder\\|rabl\\|gemspec\\|podspec\\)\\'"
  "\\(Gem\\|Rake\\|Cap\\|Thor\\|Puppet\\|Berks\\|Brew\\|Vagrant\\|Guard\\|Pod\\)file"
  :hook
  (ruby-ts-mode-hook . eglot-ensure)
  :init
  (setq ruby-align-chained-calls t)

  (with-eval-after-load 'apheleia
    (cl-pushnew '(ruby-ts-mode . rufo) apheleia-mode-alist :test #'equal))

  (cl-pushnew '("\\([^/]+\\)\\.rb\\'" "\\1_test.rb") find-sibling-rules :test #'equal)
  (cl-pushnew '("\\([^/]+\\)_test\\.rb\\'" "\\1.rb") find-sibling-rules :test #'equal)
  :hook
  (ruby-ts-mode-hook . init-ruby-ts-hideshow-setup)
  :config
  (defun init-ruby-ts-hideshow-setup ()
    (setq-local hs-block-start-regexp (rx (or "def" "class" "module" "do" "{" "["))
                hs-block-end-regexp (rx (or "}" "]" "end"))
                hs-forward-sexp-function #'forward-sexp
                hs-c-start-regexp (rx bol (or (+ (zero-or-more blank) "#") "=begin")))))
#+end_src

*** Packages
**** inf-ruby
#+begin_src emacs-lisp :tangle yes
(use-package inf-ruby
  :ensure t
  :hook
  (ruby-ts-mode-hook . inf-ruby-minor-mode)
  ;; Auto breakpoint
  (compilation-filter-hook . inf-ruby-auto-enter)
  :init
  (setq inf-ruby-default-implementation "pry")

  (with-eval-after-load 'repl-toggle
    (cl-pushnew '(ruby-ts-mode . inf-ruby) rtog/mode-repl-alist :test #'equal)))
#+end_src

**** minitest
#+begin_src emacs-lisp :tangle yes
(use-package minitest
  :ensure t
  :hook
  (ruby-ts-mode-hook . minitest-enable-appropriate-mode)
  :bind
  (:map minitest-mode-map
        ("C-. t a" . minitest-verify-all)
        ("C-. t f" . minitest-verify)
        ("C-. t t" . minitest-verify-single))
  :init
  (put 'minitest-use-bundler 'safe-local-variable 'booleanp))
#+end_src

** rust
#+begin_src emacs-lisp :tangle yes
(use-package rust-ts-mode
  :mode "\\.rs\\'"
  :hook
  (rust-ts-mode-hook . eglot-ensure)
  :init
  (with-eval-after-load 'org
    (cl-pushnew '("rust" . rust-ts-mode) org-src-lang-modes :test #'equal)))
#+end_src

** scala
#+begin_src emacs-lisp :tangle yes
(use-package scala-mode
  :ensure t
  :mode "\\.\\(scala\\|sbt\\)\\'"
  :hook
  (scala-mode-hook . init-scala-prettify-symbols)
  :init
  (setq scala-indent:align-parameters t)
  :config
  (defun init-scala-prettify-symbols ()
    (setq prettify-symbols-alist scala-prettify-symbols-alist)
    (prettify-symbols-mode 1)))
#+end_src

** sh
#+begin_src emacs-lisp :tangle yes
(use-package sh-script
  :hook
  (sh-mode-hook . flymake-mode)
  :bind
  (:map sh-mode-map ("M-g k" . man))
  :init
  ;; Use regular indentation for line-continuation
  (setq sh-indent-after-continuation 'always)

  (cl-pushnew '("\\([^/]+\\)\\.sh\\'" "\\1.bats") find-sibling-rules :test #'equal)
  (cl-pushnew '("\\([^/]+\\)\\.bats\\'" "\\1.sh") find-sibling-rules :test #'equal)

  (with-eval-after-load 'apheleia
    (cl-pushnew '(sh-mode . shfmt) apheleia-mode-alist :test #'equal)))
#+end_src

*** Packages
** sql
#+begin_src emacs-lisp :tangle yes
(use-package sql
  :init
  (setq sql-mysql-options '("--protocol=tcp" "--prompt=" "--disable-pager")))
#+end_src

** terraform
#+begin_src emacs-lisp :tangle yes
(use-package terraform-mode
  :ensure t
  :mode "\\.tf$"
  :hook
  (terraform-mode-hook . terraform-mode-set-devdocs)
  :config
  (defun terraform-mode-set-devdocs ()
    (setq-local devdocs-current-docs '("terraform"))))
#+end_src

** typescript
#+begin_src emacs-lisp :tangle yes
(use-package typescript-ts-mode
  :mode "\\.ts$"
  :hook
  (typescript-ts-mode-hook . eglot-ensure)
  :init
  (cl-pushnew '("\\([^/]+\\)\\.ts\\'" "\\1.test.ts") find-sibling-rules :test #'equal)
  (cl-pushnew '("\\([^/]+\\).test\\.ts\\'" "\\1.ts") find-sibling-rules :test #'equal))
#+end_src

** web
*** Packages
**** cakecrumbs
Display current path for HTML/XML/CSS.
#+begin_src emacs-lisp :tangle no
(use-package cakecrumbs
  :ensure t
  :defer 3
  :commands
  (cakecrumbs-auto-setup)
  :config
  (cakecrumbs-auto-setup))
#+end_src

**** web-mode
#+begin_src emacs-lisp :tangle no
(use-package web-mode
  :ensure t
  :mode "\\.\\(phtml\\|php\\|[agj]sp\\|as[cp]x\\|erb\\|djhtml\\|html?\\|hbs\\|ejs\\|jade\\|swig\\|tmpl\\)\\'"
  :init
  (setq web-mode-enable-html-entities-fontification t)
  ;; Highlight enclosing tags of the element under cursor
  (setq web-mode-enable-current-element-highlight t)
  ;; No extra indentation for blocks.
  (setq web-mode-script-padding 0)
  (setq web-mode-style-padding 0))
#+end_src

** yaml
#+begin_src emacs-lisp :tangle yes
(use-package yaml-ts-mode
  :mode "\\.\\(e?ya?\\|ra\\)ml\\'"
  :hook
  (yaml-ts-mode-hook . apheleia-mode)
  (yaml-ts-mode-hook . indent-bars-mode)
  (yaml-ts-mode-hook . (lambda () (variable-pitch-mode 0))))
#+end_src

* Tools
** Focus
Dim out the surrounding text except the current focused paragraph or expression.
#+begin_src emacs-lisp :tangle yes
(use-package focus :ensure t)
#+end_src

** Indentation guides
#+begin_src emacs-lisp :tangle yes
(use-package indent-bars
  :ensure t
  :init
  (setq indent-bars-width-frac 0.2))
#+end_src

** Pomodoro
#+begin_src emacs-lisp :tangle yes
(use-package pomm
  :ensure t
  :commands (pomm pomm-third-time)
  :bind
  (:map leader-map ("p" . pomm-third-time)))
#+end_src

** Session
Support for restoring previous session on Emacs restart. This adds a few tweaks that makes
it really useful.
- Support for persistent undo history via ~buffer-undo-list~.
- Ignore saving of =gz= files as it seems not to properly load those.
- Restore no buffers to begin with to avoid slow starts with huge sessions.
#+begin_src emacs-lisp :tangle yes
(use-package desktop
  :hook
  (server-after-make-frame-hook . desktop-read)
  (server-after-make-frame-hook . desktop-save-mode)
  :init
  (setq desktop-save t)
  (setq desktop-files-not-to-save "\\(\\`/[^/:]*:\\|(ftp)\\|\\.gz\\'\\)")

  (setq desktop-load-locked-desktop t)
  (setq desktop-restore-eager 0)
  (setq desktop-restore-frames nil)

  (desktop-save-mode 1)
  :config
  (dolist (var '(compile-history
                 extended-command-history
                 grep-history
                 minibuffer-history
                 query-replace-history
                 read-expression-history
                 shell-command-history))
    (cl-pushnew var desktop-globals-to-save :test #'equal))
  (dolist (var '(apheleia-formatter buffer-undo-list comint-input-ring))
    (cl-pushnew var desktop-locals-to-save :test #'equal)))
#+end_src

** Speed reading
#+begin_src emacs-lisp :tangle yes
(use-package spray
  :ensure t
  :init
  (setq spray-height 500)
  (setq spray-margin-left 2)

  (keymap-set toggle-map "s" '("Speed reading" . spray-mode))
  :config
  (setq spray-unsupported-minor-modes
        (append '(beacon-mode centered-window-mode visual-fill-column-mode)
                spray-unsupported-minor-modes)))
#+end_src

** Spell checking
#+begin_src emacs-lisp :tangle yes
(defvar ispell-alternate-dictionary)
(setq ispell-alternate-dictionary (getenv "WORDLIST"))

(use-package jinx
  :ensure t
  :hook
  ((prog-mode-hook
    text-mode-hook) . jinx-mode)
  :bind
  (([remap ispell-word] . jinx-correct)
   (:repeat-map jinx-repeat-map
                ("s" . jinx-next)
                ("S" . jinx-previous)
                ("[" . jinx-previous)
                ("]" . jinx-next)))
  :init
  (keymap-set next-map "s" '("Misspelling" . jinx-next))
  (keymap-set prev-map "s" '("Misspelling" . jinx-previous)))
#+end_src

Automatic language detection that updates the spell checker.
#+begin_src emacs-lisp :tangle no
(use-package guess-language
  :ensure t
  :commands
  (guess-language-mode))
#+end_src

** Terminal
*** Color
Avoid color when possible.
#+begin_src emacs-lisp :tangle yes
(setenv "NO_COLOR" "1")
#+end_src

*** eshell
#+begin_src emacs-lisp :tangle yes
(use-package eshell
  :preface
  (eval-when-compile
    (require 'em-hist)
    (require 'em-term))
  (defvar eshell-visual-commands)
  :hook
  (eshell-mode-hook . abbrev-mode)
  (eshell-mode-hook . init-eshell-define-keys)
  (eshell-mode-hook . init-eshell-set-visual-commands)
  :bind
  (("C-!" . eshell)
   (:map leader-map ("t" . eshell)))
  :init
  (setq eshell-buffer-maximum-lines 20000)
  (setq eshell-history-size 10000)
  (setq eshell-hist-ignoredups t)
  (setq eshell-save-history-on-exit t)
  (setq eshell-error-if-no-glob t)
  (setq eshell-destroy-buffer-when-process-dies t)
  (autoload 'eshell-smart-initialize "em-smart")
  :config
  (defun init-eshell-define-keys ()
    (keymap-set eshell-mode-map "RET" '+eshell-expand-abbrev-and-send-input))

  (defun init-eshell-set-visual-commands ()
    (setq eshell-visual-commands
          (append
           '("fish" "ghcid" "jshell" "most" "ssh" "tail" "tsun" "watch")
           eshell-visual-commands))))
#+end_src

**** Abbreviations
Fish-like abbreviations that expand on space or enter.
#+begin_src emacs-lisp :tangle yes
(use-package eshell
  :functions
  (+eshell-abbrev-expand-p)
  :preface
  (autoload 'eshell-parse-arguments "esh-arg")
  (autoload 'eshell-send-input "esh-mode")
  :init
  (define-abbrev-table 'eshell-mode-abbrev-table
    '(("base64" "base64 -w0")
      ("e" "find-file-other-window")
      ("E" "dired")
      ("gco" "git checkout")
      ("gd" "magit-diff-unstaged")
      ("gds" "magit-diff-staged")
      ("gs" "magit-status")
      ("time" "time -p")
      ("tree" "tree -a")
      ("week" "date '+%V'"))
    :enable-function #'+eshell-abbrev-expand-p)
  :config
  (defun +eshell-expand-abbrev-and-send-input ()
    "Expand abbreviation and send input to `eshell'."
    (interactive)
    (expand-abbrev)
    (call-interactively #'eshell-send-input))

  (defun +eshell-abbrev-expand-p ()
    "Return t if abbreviation should be expanded.
Expansion should happen when abbreviation is at the beginning of
the line or after an eshell operator."
    (let* ((end (point-marker))
           (begin (save-excursion (beginning-of-line) (point)))
           (args (catch 'eshell-incomplete (eshell-parse-arguments begin end))))
      (or (= 1 (length args))
          (let ((last-two-args (last args 2)))
            (and (consp (car last-two-args))
                 (eq (caar last-two-args)
                     'eshell-operator)))))))
#+end_src

**** Packages
***** eshell-fringe-status
Command execution status indicator in the fringe.
#+begin_src emacs-lisp :tangle no
(use-package eshell-fringe-status
  :ensure t
  :hook
  (eshell-mode-hook . eshell-fringe-status-mode))
#+end_src

*** vterm
Fast terminal emulator utilizing =libvterm=.
#+begin_src emacs-lisp :tangle yes
(use-package vterm
  :ensure t
  :bind
  (:map leader-map ("T" . vterm))
  :init
  (setq vterm-max-scrollback 100000)
  (setq vterm-shell "fish"))
#+end_src

**** Packages
***** vterm-toggle
#+begin_src emacs-lisp :tangle yes
(use-package vterm-toggle
  :ensure t
  :bind
  (("C-M-!" . vterm-toggle)
   (:map vterm-mode-map ("C-<return>" . vterm-toggle-insert-cd))))
#+end_src

** Undo
Increase undo limit.
#+begin_src emacs-lisp :tangle yes
(setq undo-limit 1000000)
(setq undo-strong-limit (* 2 undo-limit))
#+end_src

*** vundo
Visual undo history navigation.
#+begin_src emacs-lisp :tangle yes
(use-package vundo
  :ensure t
  :bind
  (:map leader-map ("u" . vundo)))
#+end_src

** AI
*** eca
#+begin_src emacs-lisp :tangle yes
(use-package eca
  :ensure t
  :bind
  (:map leader-map ("e" . eca)))
#+end_src

*** gptel
#+begin_src emacs-lisp :tangle yes
(use-package gptel
  :ensure t
  :bind
  (:map leader-map
        ("g" . gptel)
        ("G" . gptel-menu))
  :functions
  (gptel-make-gh-copilot gptel-make-ollama gptel-make-openai)
  :config
  (gptel-make-gh-copilot "Copilot")
  (gptel-make-ollama "Ollama"
    :host "localhost:11434"
    :stream t
    :models '("gemma4"
              "gpt-oss"
              "qwen3"))
  (gptel-make-openai "OpenCode-Zen"
    :host "opencode.ai"
    :endpoint "/zen/v1/chat/completions"
    :stream t
    ;; https://opencode.ai/zen/v1/models
    :models '(deepseek-v4-flash-free
              qwen3.6-plus-free))

  (setq gptel-model 'gpt-4.1)
  (setq gptel-backend (gptel-make-gh-copilot "Copilot"))
  (setq gptel-default-mode 'org-mode)

  (cl-pushnew
   '(cli . "You are a large language model and a correct command line helper. Provide commands and only commands as output without any additional text, prompt or note.")
   gptel-directives
   :test #'equal)

  (cl-pushnew
   '(regex . "You are a large language model and regular expression expert. Respond with pure regular expressions without involvement of other programming languages.")
   gptel-directives
   :test #'equal)

  (cl-pushnew
   '(commit . "You are an expert at writing Git commits. Your job is to write a short clear commit message that summarizes the changes.

The commit message should be structured as follows:

    <type>(<optional scope>): <description>

    [optional body]

- Commits MUST be prefixed with a type, which consists of one of the followings words: build, chore, ci, docs, feat, fix, perf, refactor, style, test
- The type feat MUST be used when a commit adds a new feature
- The type fix MUST be used when a commit represents a bug fix
- An optional scope MAY be provided after a type. A scope is a phrase describing a section of the codebase enclosed in parenthesis, e.g., fix(parser):
- A description MUST immediately follow the type/scope prefix. The description is a short description of the code changes, e.g., fix: array parsing issue when multiple spaces were contained in string.
- Try to limit the whole subject line to 60 characters
- Lowercase the subject line
- Do not end the subject line with any punctuation
- A longer commit body MAY be provided after the short description, providing additional contextual information about the code changes. The body MUST begin one blank line after the description.
- Use the imperative mood in the subject line
- Keep the body short and concise (omit it entirely if not useful)")
   gptel-directives
   :test #'equal))

(use-package gptel-agent
  :ensure t
  :commands
  (gptel-agent-update)
  :config
  (cl-pushnew "~/.config/opencode/skills/" gptel-agent-skill-dirs :test #'equal)
  (gptel-agent-update))

(use-package gptel-autocomplete
  :ensure t
  :commands
  (gptel-autocomplete-mode gptel-accept-completion)
  :bind
  (:map gptel-autocomplete-completion-map
        ("C-e" . 'gptel-accept-completion))
  :init
  (setq gptel-autocomplete-idle-delay 0.1)
  (keymap-set toggle-map "," '("GPTel completion" . gptel-autocomplete-mode)))

(use-package gptel-magit
  :ensure t
  :after magit
  :hook
  (magit-mode-hook . gptel-magit-install)
  :bind
  (:map git-commit-mode-map ("M-g" . gptel-magit-generate-message-from-buffer))
  :functions
  (gptel-magit--request
   gptel-magit--format-commit-message)
  :config
  (defun gptel-magit-generate-message-from-buffer ()
    "Generate a commit message when in the git commit buffer."
    (interactive)
    (unless (magit-commit-message-buffer)
      (user-error "No commit in progress"))
    (gptel-magit--request (buffer-string)
                          :system gptel-magit-commit-prompt
                          :context nil
                          :callback (lambda (response _info)
                                      (let ((msg (gptel-magit--format-commit-message response)))
                                        (with-current-buffer (magit-commit-message-buffer)
                                          (save-excursion
                                            (goto-char (point-min))
                                            (insert msg))))))
    (message "magit-gptel: Generating commit message...")))

(use-package gptel-quick
  :ensure t
  :bind
  (:map help-map ("," . gptel-quick)))
#+end_src

*** macher
#+begin_src emacs-lisp :tangle yes
(use-package macher
  :ensure t
  :after gptel
  :functions
  (macher-install)
  :config
  (macher-install)
  :bind
  (:map local-leader-map ("g" . macher-implement)))
#+end_src

* Testing
Packages that I am currently testing or evaluating.

** annotate
#+begin_src emacs-lisp :tangle yes
(use-package annotate
  :ensure t
  :commands
  (annotate-mode)
  :init
  (keymap-set toggle-map "a" '("Annotate" . annotate-mode)))
#+end_src

** consult-git-log-grep
#+begin_src emacs-lisp :tangle yes
(use-package consult-git-log-grep
  :ensure t
  :after magit
  :init
  (setq consult-git-log-grep-open-function #'magit-show-commit))
#+end_src

** dape
#+begin_src emacs-lisp :tangle no
(use-package dape :ensure t)
#+end_src

** elfeed
#+begin_src emacs-lisp :tangle no
(use-package elfeed :ensure t)
#+end_src

** freeze-it
#+begin_src emacs-lisp :tangle no
(use-package freeze-it :ensure t)
#+end_src

** opencode
#+begin_src emacs-lisp :tangle yes
(use-package opencode :ensure t)
#+end_src

** mcp
#+begin_src emacs-lisp :tangle yes
(use-package mcp :ensure t)
#+end_src

* Private
#+begin_src emacs-lisp :tangle yes
(require 'private nil t)
#+end_src

* Documentation
** Keybindings
I am trying to reduce the amount of custom keybindings, therefore I present a table of default keybindings in case I would forget them.

*** Aliases
| Keybinding | Alternative |
|------------+-------------|
| =M-=         | =C-[=         |
| =TAB=        | =C-i=         |
| =RET=        | =C-m=         |

*** Commands
| Keybinding      | Function                          | Description                                                                      |
|-----------------+-----------------------------------+----------------------------------------------------------------------------------|
| =C-o=             | ~open-line~                         | Open line above                                                                  |
| =C-j=             | ~electric-newline-and-maybe-indent~ | Add newline and indent if needed                                                 |
| =M-SPC=           | ~just-one-space~                    | Ensures just one space                                                           |
| =M-\=             | ~delete-horizontal-space~           | Delete all space                                                                 |
| =M-^=             | ~delete-indentation~                | Join current line with previous line                                             |
| =M-z=             | ~zap-to-char~                       | Delete until character                                                           |
| =C-S-backspace=   | ~kill-whole-line~                   | Kill entire lines, can be used to move several lines at once                     |
| =M-/=             | ~dabbrev-expand~                    | Abbreviation completion                                                          |
| =M-tab= / =C-[ C-i= | ~completion-at-point~               | More context aware completion                                                    |
| =M-s .=           | ~isearch-forward-symbol-at-point~   | Search for the symbol at point                                                   |
| =C-w=             | ~kill-region~                       | Cut                                                                              |
| =M-w=             | ~kill-ring-save~                    | Copy                                                                             |
| =C-y=             | ~yank~                              | Paste                                                                            |
| =M-y=             | ~yank-next~                         | Paste (next item)                                                                |
| =C-x SPC=         | ~rectangle-mark-mode~               | Rectangular selection                                                            |
| =C-x r t=         | ~string-rectangle~                  | Insert string in beginning of rectangular selection (=C-t= in =rectangle-mark-mode=) |
| =C-M-f=           | ~forward-sexp~                      | Move forward inside a balanced expression                                        |
| =C-M-b=           | ~backward-sexp~                     | Move backward inside a balanced expression                                       |
| =C-M-n=           | ~forward-list~                      | Move forward across one balanced group of parenthesis                            |
| =C-M-p=           | ~backward-list~                     | Move backward across one balanced group of parenthesis                           |
| =M-m=             | ~back-to-indentation~               | Move to the first non-whitespace character                                       |
| =M-e=             | ~forward-sentence~                  | End of sentence                                                                  |
| =M-a=             | ~backward-sentence~                 | Start of sentence                                                                |
| =C-M-d=           | ~down-list~                         | Move forward down one level of parenthesis                                       |
| =C-M-u=           | ~backward-up-list~                  | Move backward out of one level of parenthesis                                    |
| =C-u C-x $=       | ~set-selective-display~             | Hide/show indentation level                                                      |
| =C-x r w= <reg>   | ~window-configuration-to-register~  | Store the window configuration of the selected frame in REGISTER                 |
| =C-x r j=         | ~jump-to-register~                  | Go to location stored in REGISTER, or restore configuration stored there         |
| =C-x C-M-0=       | ~global-text-scale-adjust~          | Change font size of all faces                                                    |
| =M-h=             | ~mark-paragraph~                    | Mark paragraph                                                                   |
| =C-SPC=           | ~set-mark-command~                  | Set a mark                                                                       |
| =C-u C-SPC=       | ~pop-to-mark-command~               | Pop a mark                                                                       |
| =C-x $=           | ~set-selective-display~             | Code-folding via indentation                                                     |
| =C-x p !=         | ~project-shell-command~             | Run shell command in the current project’s root directory                        |
| =C-x p &=         | ~project-async-shell-command~       | Run shell command asynchronously in the current project’s root directory         |

*** Gnus
| Keybinding | Function                              | Description                                                   |
|------------+---------------------------------------+---------------------------------------------------------------|
| =L=          | ~gnus-group-list-all-groups~            | List all groups (works together with prefix to provide level) |
| =RET=        | ~gnus-topic-select-group~               | List group mails (works together with prefix to see all)      |
| =g=          | ~gnus-group-get-new-news~               | Refresh groups list                                           |
| =G G=        | ~gnus-group-make-nnir-group~            | Search mails at server side                                   |
| =#=          | ~gnus-group-mark-group~                 | Mark                                                          |
| =M-#=        | ~gnus-group-unmark-group~               | Un-mark                                                       |
| =R=          | ~gnus-summary-reply-with-original~      | Reply w/ quoted text                                          |
| =r=          | ~gnus-summary-reply~                    | Reploy w/o quoted text                                        |
| =S W=        | ~gnus-summary-wide-reply-with-original~ | Reply all w/ quoted text                                      |
| =S w=        | ~gnus-summary-wide-reply~               | Reply all w/o quoted text                                     |
| =m= / =C-x m=  | ~gnus-new-mail~                         | Compose new mail                                              |
| =S D e=      | ~gnus-summary-resend-message-edit~      | Re-send a mail in Draft folder                                |
| =C-c C-a=    | ~mml-attach-file~                       | Attach a file                                                 |
| =o=          | ~gnus-mime-save-part~                   | Save attachment                                               |
| =C-c C-f=    | ~gnus-summary-mail-forward~             | Forward mail                                                  |
| =c=          | ~gnus-group-catchup-current~            | Mark mails as read                                            |

*** Org
| Keybinding | Function             | Description                                    |
|------------+----------------------+------------------------------------------------|
| =C-c C-w=    | ~org-refile~           | Move headline under another top level headline |
| =<S-right>=  | ~org-shiftright~       | Cycle through todo keywords (right)            |
| =<S-left>=   | ~org-shiftleft~        | Cycle through todo keywords (left)             |
| =C-c C-c=    | ~org-ctrl-c-ctrl-c~    | Set tags for an item                           |
| =C-c C-t=    | ~org-todo~             | Set tags for an item (via menu)                |
| =C-c C-s=    | ~org-schedule~         | Schedule an item                               |
| =C-c C-d=    | ~org-deadline~         | Set a deadline                                 |
| =C-c C-q=    | ~org-set-tags-command~ | Attach tags to item                            |

** Tasks
*** TODO Improve bindings
- Group editing bindings under logical prefix
- magit-diff-range (code review)
- Binding for
  - org-store-link
  - leader mnemonics? as a separate package, or is it too much work?
    - (t)oggle
    - (s)earch
    - (f)ile
      - (r)ecent
      - (s)ave
    - (b)uffer
      - b - switch
      - (k)ill
      - (n)ext/(p)rev - repeat
      - (r)evert
      - (j)ump (bookmark)
*** TODO Split theme into several parts
- One theme to set big headers and such things
- One theme to highlight errors
- One theme for the mode-line
*** TODO Evaluate =windmove=
- Perhaps with ~windmove-create-window t~
*** TODO Extract history persistence code (=persistory=)
- comint
- ielm (via comint and savehist)
- haskell-interactive (via savehist)
*** TODO Use ~vc-mode~ commands more =C-x v g=, =C-x v v= and =C-x v ==.
- Also ~vc-dir~ and the ~vc-git-stash~ (can stash individual files)
- Diff merge-base =C-x v M D=, =C-x v M L=
- ~vc-log-search~
*** TODO Use ChangeLog entries =C-x 4 a=
*** TODO Create macros via checking =C-h l= (~view-lossage~) then it can be used together with ~edit-last-kbd-macro~.
*** TODO Is it useful to use ~highlight-regexp~?
*** TODO Evaluate skeleton
*** TODO Evaluate [[https://github.com/alphapapa/obvious.el][obvious.el]]
*** TODO Prefix function that overrides ~kill-region~ as ~kill-ring-save~ for next command
*** TODO Function to jump to file under point, preferably with support for : line number
*** TODO Function to load theme defined in current file
*** TODO Split configuration into essential and full feature
- emacs-minimal (no extra packages? or favorites)
- emacs (minimum amount of packages)
- emacs-full (all packages)

**** Minimal
- apheleia
- consult
- corfu
- deadgrep
- dtrt-indent
- dumb-jump
- embark
- envrc
- marginalia
- markdown-mode
- nix-ts-mode
- orderless
- popper
- puni
- readable-mono-theme
- readable-typo-theme
- transpose-frame
- vertico
- vundo

**** Regular
- adaptive-wrap
- all-the-icons
- all-the-icons-completion
- all-the-icons-dired
- avy
- cape
- d2-mode
- devdocs
- diff-hl
- dired-sidebar
- dired-subtree
- dslide
- easy-escape
- epithet
- gcmh
- ghelp
- git-modes
- goto-chg
- grugru
- helpful
- hide-mode-line
- idle-highlight-mode
- imenu-anywhere
- imenu-extra
- imenu-list
- indent-bars
- indent-info
- jinx
- link-hint
- magit
- miniedit
- nix-mode
- no-littering
- noccur
- nov
- org-appear
- org-limit-image-size
- org-modern
- pdf-tools
- rainbow-delimiters
- repl-toggle
- separedit
- tempel
- terraform-mode
- virtual-auto-fill
- visual-fill-column
- visual-replace
- vterm
- ws-butler (deprecate?)

**** Full
- browse-at-remote
- cakecrumbs
- caml
- cider
- clojure-mode
- cobol-mode
- color-identifiers-mode
- cov
- crystal-mode
- csv-mode
- dired-git-info
- docker
- elm-mode
- erlang
- eshell-fringe-status
- explain-pause-mode
- fish-mode
- flymake-racket
- focus
- gotest
- gradle-mode
- groovy-mode
- guess-language
- haskell-mode
- hl-todo
- inf-crystal
- inf-ruby
- json-navigator
- kind-icon
- kotlin-mode
- kubernetes
- ligature
- lte
- mermaid-mode
- minitest
- nginx-mode
- ob-http
- org-cliplink
- org-contrib
- org-noter
- outline-minor-faces
- ox-epub
- package-lint-flymake
- page-break-lines
- plantuml-mode
- pomm
- protobuf-mode
- python-test
- racket-mode
- rego-mode
- rspec-mode
- scala-mode
- sly
- spray
- string-inflection
- stripe-buffer
- tuareg
- valign
- vlf
- vterm-toggle
- web-mode

# Local Variables:
# jinx-local-words: "Terje Terje's minibuffer"
# End:
