<commit_before>#+TITLE: Emacs Configuration
#+AUTHOR: <NAME>
#+PROPERTY: header-args:emacs-lisp :tangle yes :results silent

* Introduction
  terlar's Emacs Configuration. It is not very well documented right now, but hopefully I
  will have time to write down some more details later.

  The rough details:
  - theme :: [[https://github.com/11111000000/tao-theme-emacs][tao-theme]] (yang) with [[file:lisp/local-theme.el][some local modifications]]
    - fonts :: [[https://github.com/be5invis/Iosevka][Iosevka Slab]], Noto Sans, Noto Serif
    - semantic highlighting :: [[https://github.com/ankurdave/color-identifiers-mode][color-identifiers-mode]], [[https://github.com/Fanael/rainbow-identifiers][rainbow-identifiers]] (fallback, with wider support)
  - menu completion :: [[https://github.com/abo-abo/swiper][ivy]]
  - +buffer completion :: [[https://github.com/company-mode/company-mode][company-mode]], [[https://github.com/tumashu/company-childframe][company-childframe]]+
  - +modal editing (vi) :: [[https://github.com/emacs-evil/evil][evil]]+
  - package configuration :: [[https://github.com/raxod502/straight.el][straight.el]], [[https://github.com/jwiegley/use-package][use-package]]
  - key definitions :: [[https://github.com/noctuid/general.el][general]]
  - code navigation :: [[https://github.com/jacktasia/dumb-jump][dumb-jump]], [[https://github.com/jojojames/smart-jump][smart-jump]], [[https://github.com/iqbalansari/emacs-source-peek][source-peek]]
  - syntax checker :: flymake

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

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

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

* The beginning
  Every good file starts with a nice header and enable lexical scoping as per the
  recommendations of the [[https://github.com/bbatsov/emacs-lisp-style-guide#source-code-layout--organization][The Emacs Lisp Style Guide]].
  #+BEGIN_SRC emacs-lisp
  ;;; init.el --- Emacs Initialization -*- lexical-binding: t; -*-
  #+END_SRC

** Startup
   Time the startup.
   #+BEGIN_SRC emacs-lisp
   (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.
   #+BEGIN_SRC emacs-lisp
   (let ((normal-gc-cons-threshold 800000)
         (normal-gc-cons-percentage 0.1)
         (normal-file-name-handler-alist file-name-handler-alist)
         (init-gc-cons-threshold 402653184)
         (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

** Variables
   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
   (eval-and-compile
     (defvar +debug-mode
       (or (getenv "DEBUG") init-file-debug)
       "Debug mode, enable through DEBUG=1 or use --debug-init.")
     (setq debug-on-error (and (not noninteractive) +debug-mode)))
   #+END_SRC

   Provide a location where Emacs can store data and cache.
   #+BEGIN_SRC emacs-lisp
   (eval-and-compile
     (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."))
   #+END_SRC

** Quiet startup
   #+BEGIN_SRC emacs-lisp
   (setq inhibit-default-init t
         inhibit-startup-screen t
         initial-major-mode 'fundamental-mode
         initial-scratch-message nil)
   #+END_SRC

** Disable GUI components
   #+BEGIN_SRC emacs-lisp
   (when window-system
     (setq use-dialog-box nil)
     (menu-bar-mode 0)
     (scroll-bar-mode 0)
     (tool-bar-mode 0)
     (tooltip-mode 0))
   #+END_SRC

** UX
   Disable bell (both visual and audible)
   #+BEGIN_SRC emacs-lisp
   (setq ring-bell-function #'ignore
         visible-bell nil)
   #+END_SRC

   Use =y= / =n= instead of =yes= / =no=
   #+BEGIN_SRC emacs-lisp
   (setq confirm-kill-emacs #'y-or-n-p)
   (fset #'yes-or-no-p #'y-or-n-p)
   #+END_SRC

** Custom file
   Put custom defs in a temporary file so it doesn't grow over time.
   #+BEGIN_SRC emacs-lisp
   (setq custom-file
         (expand-file-name "custom.el" temporary-file-directory))
   #+END_SRC

** Emacs source
   Provide a location for the Emacs source in case I want to look up the
   function definitions of some of the C functions.
   #+BEGIN_SRC emacs-lisp
   (setq source-directory (expand-file-name "emacs" "~/src/git.sv.gnu.org"))
   #+END_SRC

** Load path
   #+BEGIN_SRC emacs-lisp
   (eval-and-compile
     (setq load-path
           (append (delete-dups load-path)
                   (list (expand-file-name "lisp" user-emacs-directory)))))
   #+END_SRC

** OS
*** macOS
    Setup PATH correctly.
    #+BEGIN_SRC emacs-lisp
    (when (eq system-type 'darwin)
      (setenv "PATH" (shell-command-to-string "launchctl getenv PATH")))
    #+END_SRC

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

** Package management
   Don't use defualt =package.el=:
   #+BEGIN_SRC emacs-lisp
   (setq package-enable-at-startup nil)
   #+END_SRC

*** straight.el
    Install =straight.el= to manage packages. Although it is horribly slow to
    initialize (epkgs I'm looking at you) it lets me avoid all the mess that is
    require to get an efficient startup with the built-in =package.el=.
    #+BEGIN_SRC emacs-lisp
    (defvar straight-recipes-gnu-elpa-use-mirror t)
    (eval-and-compile
      (defvar bootstrap-version)
      (let ((bootstrap-file
             (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
            (bootstrap-version 5))
        (unless (file-exists-p bootstrap-file)
          (with-current-buffer
              (url-retrieve-synchronously
               "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
               'silent 'inhibit-cookies)
            (goto-char (point-max))
            (eval-print-last-sexp)))
        (load bootstrap-file nil 'nomessage)))

    (setq straight-cache-autoloads t
          straight-use-package-by-default t
          straight-vc-git-default-protocol 'https
          straight-vc-git-force-protocol t)
    #+END_SRC

*** use-package
    #+BEGIN_SRC emacs-lisp
    (eval-and-compile
      (straight-use-package 'use-package)
      (require 'use-package)
      (setq use-package-always-defer t)
      (if +debug-mode
          (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

** Packages
*** no-littering
    Help keeping =~/.emacs.d= clean.
    #+BEGIN_SRC emacs-lisp
    (use-package no-littering
      :demand t
      :preface
      (defvar no-littering-etc-directory +data-dir)
      (defvar no-littering-var-directory +cache-dir))
    #+END_SRC

*** exec-path-from-shell
    #+BEGIN_SRC emacs-lisp
    (use-package exec-path-from-shell
      :demand t
      :custom
      (exec-path-from-shell-check-startup-files nil)
      :config
      (exec-path-from-shell-initialize))
    #+END_SRC

*** server
    #+BEGIN_SRC emacs-lisp
    (use-package server
      :straight nil
      :defer 3
      :config
      (unless (server-running-p)
        (server-start)))
    #+END_SRC

*** savehist
    Persist history.
    #+BEGIN_SRC emacs-lisp
    (use-package savehist
      :straight nil
      :hook
      (after-init . savehist-mode)
      :custom
      (savehist-additional-variables '(kill-ring
                                       search-ring
                                       regexp-search-ring))
      (savehist-autosave-interval 60)
      (savehist-save-minibuffer-history t))
    #+END_SRC

*** saveplace
    Keep track of last point place to resume editing in the same file.
    #+BEGIN_SRC emacs-lisp
    (use-package saveplace
      :straight nil
      :defer 5
      :config
      (save-place-mode 1))
    #+END_SRC

** Libraries
*** cl-lib
    #+BEGIN_SRC emacs-lisp
    (require 'cl-lib)
    #+END_SRC

*** async
    #+BEGIN_SRC emacs-lisp
    (use-package async
      :demand t)
    #+END_SRC

* Base
** Performance
   Disable bidirectional text for tiny performance boost
   #+BEGIN_SRC emacs-lisp
   (setq-default bidi-display-reordering nil)
   #+END_SRC

   Update UI less frequently
   #+BEGIN_SRC emacs-lisp
   (setq idle-update-delay 2
         jit-lock-defer-time 0
         jit-lock-stealth-time 0.2
         jit-lock-stealth-verbose nil)
   #+END_SRC

** Localization
   #+BEGIN_SRC emacs-lisp
   (setq-default calendar-week-start-day 1)
   #+END_SRC

** Terminal
*** Less
    Make less work inside Emacs shells.
    #+BEGIN_SRC emacs-lisp
    (setenv "LESS" "--dumb --prompt=s")
    #+END_SRC

** Packages
*** all-the-icons
    Support functions for displaying icons. I am trying to use icons where possible.
    #+BEGIN_SRC emacs-lisp
    (use-package all-the-icons
      :demand t
      :straight
      (:host github :repo "ubolonton/all-the-icons.el" :branch "font-lock-fix" :files (:defaults "data")))
    #+END_SRC

*** auto-minor-mode
    Enable minor modes by buffer name and contents. It provides the =use-package= keyword
    =:minor= and =:magic-minor= where you can specify these rules.
    #+BEGIN_SRC emacs-lisp
    (use-package auto-minor-mode :demand t)
    #+END_SRC

*** defrepeater
    Support defining repeating commands by repeating the last key.
    #+BEGIN_SRC emacs-lisp
    (use-package defrepeater)
    #+END_SRC

*** diminish
    Hide mode-line modes, more room for important stuff.
    #+BEGIN_SRC emacs-lisp
    (use-package diminish)
    #+END_SRC

*** general
    More convenient key definitions. I provides the =use-package= keyword =:general=.
    #+BEGIN_SRC emacs-lisp
    (use-package general :demand t)
    #+END_SRC

*** minions
    #+BEGIN_SRC emacs-lisp
    (use-package minions
      :general
      ([C-S-mouse-1] 'minions-minor-modes-menu))
    #+END_SRC

*** quick-peek
    Library to display inline popups; used by =source-peek=.
     #+BEGIN_SRC emacs-lisp
    (use-package quick-peek)
     #+END_SRC

*** which-key
    Display available keybindings in a popup.
    #+BEGIN_SRC emacs-lisp
    (use-package which-key
      :defer 1
      :diminish which-key-mode
      :custom
      (which-key-sort-order #'which-key-key-order-alpha)
      (which-key-sort-uppercase-first nil)
      (which-key-add-column-padding 1)
      (which-key-min-display-lines 5)
      (which-key-idle-delay 0.5)
      :config
      (push '(("<\\([[:alnum:]-]+\\)>" . nil) . ("\\1" . nil)) which-key-replacement-alist)
      (push '(("\\`\\?\\?\\'" . nil)          . ("λ" . nil)) which-key-replacement-alist)
      (push '(("<up>"    . nil)               . ("↑" . nil)) which-key-replacement-alist)
      (push '(("<right>" . nil)               . ("→" . nil)) which-key-replacement-alist)
      (push '(("<down>"  . nil)               . ("↓" . nil)) which-key-replacement-alist)
      (push '(("<left>"  . nil)               . ("←" . nil)) which-key-replacement-alist)
      (push '(("SPC" . nil)                   . ("␣" . nil)) which-key-replacement-alist)
      (push '(("TAB" . nil)                   . ("↹" . nil)) which-key-replacement-alist)
      (push '(("RET" . nil)                   . ("⏎" . nil)) which-key-replacement-alist)
      (push '(("DEL" . nil)                   . ("⌫" . nil)) which-key-replacement-alist)
      (push '(("deletechar" . nil)            . ("⌦" . nil)) which-key-replacement-alist)

      (which-key-add-key-based-replacements
        "C-c !" "check"
        "C-c @" "outline"
        "C-c &" "snippet"
        "C-c m" "major-mode"
        "C-c W" "workspace")
      (which-key-setup-side-window-bottom)

      (which-key-mode 1))
    #+END_SRC

** Functions
   Prevent forms from producing output or other noise.
   #+BEGIN_SRC emacs-lisp
   (defmacro quiet! (&rest forms)
     "Run FORMS without making any noise."
     `(if +debug-mode
          (progn ,@forms)
        (fset '+old-write-region-fn (symbol-function 'write-region))
        (cl-letf ((standard-output (lambda (&rest _)))
                  ((symbol-function 'load-file) (lambda (file) (load file nil t)))
                  ((symbol-function 'message) (lambda (&rest _)))
                  ((symbol-function 'write-region)
                   (lambda (start end filename &optional append visit lockname mustbenew)
                     (unless visit (setq visit 'no-message))
                     (when (fboundp '+old-write-region-fn)
                       (+old-write-region-fn
                        start end filename append visit lockname mustbenew))))
                  (inhibit-message t)
                  (save-silently t))
          ,@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.

   Example:
     (advice-add #'orig-fun :around #'quiet-function-advice)"
     (quiet! (apply orig-fn args)))
   #+END_SRC

*** Process
    #+BEGIN_SRC emacs-lisp
    (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

*** Utils
    #+BEGIN_SRC emacs-lisp
    (defun work-log-file ()
      "Open today's work log file."
      (interactive)
      (let ((daily-name (format-time-string "%Y-%m-%d")))
        (find-file (expand-file-name (concat "~/org/worklog-" daily-name ".org")))))

    (defun sprint-file ()
      "Open current sprint log file."
      (interactive)
      (let ((sprint-name (format-time-string "%Y-W%W")))
        (find-file (expand-file-name (concat "~/org/sprint-" sprint-name ".org")))))
    #+END_SRC

*** Window
    #+BEGIN_SRC emacs-lisp
    (defun toggle-dedicated-window ()
      (interactive)
      (set-window-dedicated-p (selected-window)
                              (not (window-dedicated-p (selected-window)))))
    #+END_SRC

* Appearance
** Typography
*** Size
    #+BEGIN_SRC emacs-lisp
    (eval-and-compile
      (defvar +line-spacing 0.25
        "Spacing between lines.")

      (defvar +default-font-height 140
        "Default font height."))
    #+END_SRC

*** Face
    #+BEGIN_SRC emacs-lisp
    (eval-and-compile
      (defvar +fixed-pitch-font "Iosevka T Slab"
        "Font used for fixed-pitch faces.")

      (defvar +variable-pitch-font "Noto Sans"
        "Font used for variable-pitch faces.")

      (defvar +serif-font "Noto Serif"
        "Font used for serif faces.")

      (defvar +unicode-font "Noto Mono"
        "Fallback font used for unicode glyphs."))
    #+END_SRC

*** Line length
    #+BEGIN_SRC emacs-lisp
    (setq-default fill-column 90)
    #+END_SRC

** Theme
*** tao-theme (tao-yang)
    An excellent, light, uncoloured theme.
    #+BEGIN_SRC emacs-lisp
    (use-package tao-theme
      :demand t
      :custom
      (tao-theme-use-height t)
      (tao-theme-use-sepia t))

    (load-theme 'tao-yang t)
    #+END_SRC

*** Local overrides
    No theme is perfect, also this is a good place to put my own experiments.
    #+BEGIN_SRC emacs-lisp
    (require 'local-theme)
    (load-theme 'local t)
    #+END_SRC

*** Echo area
    Adjust the font face used for the echo area.
    #+BEGIN_SRC emacs-lisp
    (dolist (buffer '(" *Echo Area 0*"
                      " *Echo Area 1*"))
      (with-current-buffer (get-buffer buffer)
        (setq-local face-remapping-alist '((default (:height 0.9) variable-pitch)))))
    #+END_SRC

** Layout
*** Padding
    Add some margins to make text feel less crowded. Put fringes on the outside for the
    same reason.
    #+BEGIN_SRC emacs-lisp
    (setq-default fringes-outside-margins t
                  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
    (when (boundp 'window-divider-mode)
      (setq window-divider-default-places t
            window-divider-default-bottom-width 1
            window-divider-default-right-width 1)
      (window-divider-mode 1))
    #+END_SRC

*** Frame
    Add some padding around the whole window (=internal-border-width=) to provide some air.
    Remove GUI elements as well as maximize the window; A maximized window ensures macOS
    won't waste extra space all-though it is technically not maximized. Make the title-bar
    transparent on macOS.
    #+BEGIN_SRC emacs-lisp
    (setq default-frame-alist
          '((internal-border-width . 12)
            (vertical-scroll-bars . nil)
            (menu-bar-lines . 0)
            (tool-bar-lines . 0)))

    (when (eq system-type 'darwin)
      (setq frame-title-format nil)
      (dolist (filter '((ns-transparent-titlebar . t)
                        (ns-appearance . unbound)))
        (push filter default-frame-alist)))
    #+END_SRC

** Mode line
   I am trying to keep the original mode line as much as possible to ensure compatibility
   with various packages and features, but I have rearranged the position of things and
   also introduced a right side mode line. I have defined a helper function to fill up the
   space needed to make the mode line appear on the right side.

*** Right alignment
    Setup the right aligned mode line and helper functions to display it.
    #+BEGIN_SRC emacs-lisp
    (defvar mode-line-space
      '(:propertize
        " " display
        ((space :width 1)))
      "Space between mode line components.")

    (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)
                               ,(+ 1
                                   (* (string-width formatted-line)
                                      0.8))))))
         formatted-line)))

    (setq-default mode-line-format
                  (append
                   mode-line-format
                   '((:eval (mode-line-right)))))
    #+END_SRC

    Move default components to the right side of the mode line.
    #+BEGIN_SRC emacs-lisp
    (delete 'mode-line-position mode-line-format)
    (delete 'mode-line-mule-info mode-line-format)
    (delete 'mode-line-modes mode-line-format)

    (setq mode-line-right-format
          `(,mode-line-position
            (:eval mode-line-mule-info)
            ,mode-line-space
            ,mode-line-modes))
    #+END_SRC

*** Position
    Add position information including column and line number but skip the percentage.
    #+BEGIN_SRC emacs-lisp
    (setq mode-line-percent-position nil)
    (column-number-mode 1)
    (line-number-mode 1)
    #+END_SRC

*** Indentation
    Display information about the current indentation settings.
    #+BEGIN_SRC emacs-lisp
    (use-package indent-info
      :defer 2
      :custom
      (indent-info-insert-target 'mode-line-mule-info)
      (indent-info-prefix " ")
      (indent-info-suffix " ")
      :config
      (global-indent-info-mode 1))
    #+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
    (use-package hide-mode-line)
    #+END_SRC

*** Diminished modes
    Hide minor modes to save space.
    #+BEGIN_SRC emacs-lisp
    (diminish 'abbrev-mode)
    (diminish 'auto-fill-function)
    (diminish 'eldoc-mode)
    (with-eval-after-load 'face-remap (diminish 'buffer-face-mode))
    (with-eval-after-load 'with-editor (diminish 'with-editor-mode))
    #+END_SRC

*** Icons
**** Modified
     #+BEGIN_SRC emacs-lisp
     (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) " "))))

     (with-eval-after-load 'all-the-icons
       (setq-default mode-line-modified '((:eval (mode-line-modified-icons)))))
     #+END_SRC

**** Remote
     #+BEGIN_SRC emacs-lisp
     (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) " ")))

     (with-eval-after-load 'all-the-icons
       (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
     (defun +shorten-vc-mode-line (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 #'+shorten-vc-mode-line)
     #+END_SRC

* Display
** Frame
   Don't implicitly resize frames when changes various settings.
   #+BEGIN_SRC emacs-lisp
   (setq frame-inhibit-implied-resize t)
   #+END_SRC

** Window
*** Favor horizontal splits
    #+BEGIN_SRC emacs-lisp
    (setq split-width-threshold nil)
    #+END_SRC

*** Manage layouts
    #+BEGIN_SRC emacs-lisp
    (use-package winner
      :straight nil
      :hook
      (window-setup . winner-mode)
      :preface
      (unless (fboundp 'winner-redo-repeat)
        (defrepeater 'winner-redo-repeat #'winner-redo))
      (unless (fboundp 'winner-undo-repeat)
        (defrepeater 'winner-undo-repeat #'winner-undo))
      :general
      ([remap winner-redo] #'winner-redo-repeat
       [remap winner-undo] #'winner-undo-repeat))
    #+END_SRC

*** Navigation
    #+BEGIN_SRC emacs-lisp
    (use-package ace-window
      :custom
      (aw-ignore-current t)
      (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
      (aw-scope 'frame)
      :general
      ([remap other-window] 'ace-window))
    #+END_SRC

*** Zoom
    Zoom a window to display as a single window temporarily.
    #+BEGIN_SRC emacs-lisp
    (use-package zoom-window
      :general
      ("C-x C-z" 'zoom-window-zoom))
    #+END_SRC

** Buffer
*** Hide async shell command buffers
    #+BEGIN_SRC emacs-lisp
    (push '("^*Async Shell Command*" . (display-buffer-no-window))
          display-buffer-alist)
    #+END_SRC

*** Popups
    Always display pop up buffers at the bottom and regard all star buffers as such
    buffers.
    #+BEGIN_SRC emacs-lisp
    (push `(,(rx bos "*" (one-or-more anything) "*" (optional "<" (one-or-more anything) ">") eos)
            (display-buffer-reuse-window
             display-buffer-in-side-window)
            (reusable-frames . visible)
            (side            . bottom)
            (window-height   . 0.4))
          display-buffer-alist)
    #+END_SRC

*** Visual Fill Column
    #+BEGIN_SRC emacs-lisp
    (use-package visual-fill-column
      :hook
      (nov-mode . visual-fill-column-mode)
      :custom
      (visual-fill-column-width fill-column))
    #+END_SRC

* Editing
** Indentation
   Default to indentation by spaces, that seems to be what most languages use nowadays.
   #+BEGIN_SRC emacs-lisp
   (setq-default indent-tabs-mode nil)
   #+END_SRC

   Convert between tabs and spaces (only tabify initial whitespace)
   #+BEGIN_SRC emacs-lisp
   (setq-default tabify-regexp "^\t* [ \t]+")
   #+END_SRC

** Text flow
   Wrap at words and don't require double spaces to end a sentence.
   #+BEGIN_SRC emacs-lisp
   (setq sentence-end-double-space nil)
   (setq-default word-wrap t)
   #+END_SRC

   Disable line wrapping.
   #+BEGIN_SRC emacs-lisp
   (setq-default truncate-lines t)
   #+END_SRC

** Kill-ring
   Save clipboard contents into kill-ring before replacing them
   #+BEGIN_SRC emacs-lisp
   (setq save-interprogram-paste-before-kill t)
   #+END_SRC

** Packages
*** avy
    Jump to things.
    #+BEGIN_SRC emacs-lisp
    (use-package avy
      :custom
      (avy-all-windows nil)
      :general
      ("M-g f" 'avy-goto-char-timer))
    #+END_SRC

*** goto-addr
    Buttonize URLs and e-mail addresses in the current buffer.
    #+BEGIN_SRC emacs-lisp
    (use-package goto-addr
      :straight nil
      :hook
      (text-mode . goto-address-mode)
      (prog-mode . goto-address-prog-mode))
    #+END_SRC

*** rotate-text
    Cycle through words, symbols and patterns.
    #+BEGIN_SRC emacs-lisp
    (use-package rotate-text
      :general
      (:keymaps
       'global
       "M-n" 'rotate-text
       "M-p" 'rotate-text-backward)
      :config
      (dolist (item '(("true" "false")
                      ("assert" "refute")
                      ("yes" "no")))
        (push item rotate-text-words)))
    #+END_SRC

*** smartparens
    Auto-insert matching parenthesis and highlight matching parenthesis.
    #+BEGIN_SRC emacs-lisp
    (use-package smartparens
      :diminish
      (smartparens-mode smartparens-strict-mode)
      :hook
      (after-init . smartparens-global-strict-mode)
      (after-init . show-smartparens-global-mode)
      (eval-expression-minibuffer-setup . smartparens-strict-mode)
      :general
      (:keymaps
       'smartparens-mode-map
       "M-D" 'sp-unwrap-sexp
       "M-R" 'sp-rewrap-sexp)
      :config
      (require 'smartparens-config)
      (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil))
    #+END_SRC

*** whitespace
    Display whitespace
    #+BEGIN_SRC emacs-lisp
    (use-package whitespace
      :straight nil
      :custom
      (whitespace-line-column fill-column)
      (whitespace-style
       '(face tabs tab-mark spaces space-mark trailing lines-tail))
      (whitespace-display-mappings
       '((tab-mark ?\t [?› ?\t])
         (newline-mark 10 [?¬ 10])
         (space-mark 32 [183] [46]))))
    #+END_SRC

*** whole-line-or-region
    Cut/copy (=C-w= / =M-w=) the current line if no region is active.
    #+BEGIN_SRC emacs-lisp
    (use-package whole-line-or-region
      :diminish whole-line-or-region-local-mode
      :hook
      (after-init . whole-line-or-region-global-mode))
    #+END_SRC

*** yasnippet
    #+BEGIN_SRC emacs-lisp
    (use-package yasnippet
      :diminish yas-minor-mode
      :hook
      (after-init . yas-global-mode)
      :custom
      (yas-also-auto-indent-first-line t)
      (yas-snippet-dirs (list (expand-file-name "snippets" user-emacs-directory)))
      ;; Nested snippets
      (yas-triggers-in-field t)
      (yas-wrap-around-region t)
      :general
      (:keymaps
       'yas-minor-mode-map
       [tab] 'nil
       "TAB" 'nil)
      :init
      (setq yas-verbosity 0))

    (use-package yasnippet-snippets
      :defer 2)
    #+END_SRC

*** yatemplate
    #+BEGIN_SRC emacs-lisp
    (use-package autoinsert
      :straight nil
      :defer 1
      :custom
      (auto-insert-query nil)
      (auto-insert-alist nil)
      :config
      (auto-insert-mode 1))

    (use-package yatemplate
      :hook
      (yas-global-mode . yatemplate-fill-alist))
    #+END_SRC

** Appearance
*** Page breaks
    Display page breaks as a horizontal line.
    #+BEGIN_SRC emacs-lisp
    (use-package page-break-lines
      :defer 1
      :diminish page-break-lines-mode
      :config
      (global-page-break-lines-mode 1))
    #+END_SRC

*** Line highlight
    #+BEGIN_SRC emacs-lisp
    (use-package hl-line
      :straight nil
      :hook
      ((prog-mode conf-mode) . hl-line-mode)
      :custom
      ;; Only highlight in selected window
      (hl-line-sticky-flag nil)
      (global-hl-line-sticky-flag nil))
    #+END_SRC

* Buffers
  Prevent generation of useless lock and backup files.
  #+BEGIN_SRC emacs-lisp
  (setq create-lockfiles nil
        make-backup-files nil)
  #+END_SRC

  Don't require confirmation when opening a new buffer.
  #+BEGIN_SRC emacs-lisp
  (setq confirm-nonexistent-file-or-buffer t)
  #+END_SRC

  Remove visual indicators from non-selected windows
  #+BEGIN_SRC emacs-lisp
  (setq highlight-nonselected-windows nil)
  (setq-default cursor-in-non-selected-windows nil)
  #+END_SRC

** Auto save
   #+BEGIN_SRC emacs-lisp
   (auto-save-visited-mode 1)
   (setq save-abbrevs 'silently)
   #+END_SRC

** Auto executable scripts
   #+BEGIN_SRC emacs-lisp
   (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
   #+END_SRC

** Fringe
   Display indicator for buffer boundaries in the right fringe.
   #+BEGIN_SRC emacs-lisp
   (setq-default indicate-buffer-boundaries 'right)
   #+END_SRC

** Minibuffer
   Enable recursive minibuffers and keep the point out of the minibuffer.
   #+BEGIN_SRC emacs-lisp
   (setq enable-recursive-minibuffers t
         minibuffer-prompt-properties
         '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt))
   #+END_SRC

   Give some more room to the minbuffer.
   #+BEGIN_SRC emacs-lisp
   (setq max-mini-window-height 0.3
         resize-mini-windows 'grow-only)
   #+END_SRC

*** No fringes
    Don't show fringes in the minibuffer.
    #+BEGIN_SRC emacs-lisp
    (defun +disable-minibuffer-window-fringes ()
      "Disable the window fringes for minibuffer window."
      (set-window-fringes (minibuffer-window) 0 0 nil))
    (add-hook 'emacs-startup-hook #'+disable-minibuffer-window-fringes)
    (add-hook 'minibuffer-setup-hook #'+disable-minibuffer-window-fringes)
    #+END_SRC

*** History
    Persist minibuffer history
    #+BEGIN_SRC emacs-lisp
    (setq history-delete-duplicates t
          history-length 500)
    #+END_SRC

*** Miniedit
    Edit minibuffer in a new temporary buffer by pressing =M-C-e=.
    #+BEGIN_SRC emacs-lisp
    (use-package miniedit
      :general
      (:keymaps
       '(minibuffer-local-map
         minibuffer-local-ns-map
         minibuffer-local-completion-map
         minibuffer-local-must-match-map)
       "M-C-e" 'miniedit))
    #+END_SRC

** Scrolling
   More procedural scrolling.
   #+BEGIN_SRC emacs-lisp
   (setq auto-window-vscroll nil
         hscroll-margin 5
         hscroll-step 5
         scroll-conservatively 101
         scroll-margin 0
         scroll-preserve-screen-position t)

   (setq-default scroll-down-aggressively 0.01
                 scroll-up-aggressively 0.01)
   #+END_SRC

** Text scale
   Text scaling works across all buffers. I rarely find that I only want to change the
   text scale only for one buffer.
   #+BEGIN_SRC emacs-lisp
   (defun +text-scale-increase-across-all-buffers (orig &rest args)
     "Text scale across all buffers.
   Calls ORIG function with ARGS."
     (dolist (buffer (buffer-list))
       (with-current-buffer buffer
         (apply orig args))))
   (advice-add 'text-scale-increase :around #'+text-scale-increase-across-all-buffers)
   #+END_SRC

*** default-text-scale
    #+BEGIN_SRC emacs-lisp
    (use-package default-text-scale
      :general
      (:keymaps
       'global
       "C-M-=" 'default-text-scale-increase
       "C-M--" 'default-text-scale-decrease
       "C-M-0" 'default-text-scale-reset))
    #+END_SRC

** Packages
*** autorevert
    Revert buffers when underlying files change.
    #+BEGIN_SRC emacs-lisp
    (use-package autorevert
      :straight nil
      :defer 2
      :diminish auto-revert-mode
      :custom
      (auto-revert-verbose nil)
      :config
      (global-auto-revert-mode 1))
    #+END_SRC

*** rainbow-mode
    Display colors inline.
    #+BEGIN_SRC emacs-lisp
    (use-package rainbow-mode
      :minor
      "-theme\\.el$")
    #+END_SRC

*** readable
    #+BEGIN_SRC emacs-lisp
    (use-package readable
      :straight nil
      :diminish readable-mode
      :hook
      ((eww-mode
        markdown-mode
        nov-mode
        org-mode) . readable-mode))
    #+END_SRC

*** spray
    #+BEGIN_SRC emacs-lisp
    (use-package spray
      :custom
      (spray-height 500)
      (spray-margin-left 2)
      :config<commit_msg>buffer: ensure eldoc is shown<commit_after>#+TITLE: Emacs Configuration
#+AUTHOR: <NAME>
#+PROPERTY: header-args:emacs-lisp :tangle yes :results silent

* Introduction
  terlar's Emacs Configuration. It is not very well documented right now, but hopefully I
  will have time to write down some more details later.

  The rough details:
  - theme :: [[https://github.com/11111000000/tao-theme-emacs][tao-theme]] (yang) with [[file:lisp/local-theme.el][some local modifications]]
    - fonts :: [[https://github.com/be5invis/Iosevka][Iosevka Slab]], Noto Sans, Noto Serif
    - semantic highlighting :: [[https://github.com/ankurdave/color-identifiers-mode][color-identifiers-mode]], [[https://github.com/Fanael/rainbow-identifiers][rainbow-identifiers]] (fallback, with wider support)
  - menu completion :: [[https://github.com/abo-abo/swiper][ivy]]
  - +buffer completion :: [[https://github.com/company-mode/company-mode][company-mode]], [[https://github.com/tumashu/company-childframe][company-childframe]]+
  - +modal editing (vi) :: [[https://github.com/emacs-evil/evil][evil]]+
  - package configuration :: [[https://github.com/raxod502/straight.el][straight.el]], [[https://github.com/jwiegley/use-package][use-package]]
  - key definitions :: [[https://github.com/noctuid/general.el][general]]
  - code navigation :: [[https://github.com/jacktasia/dumb-jump][dumb-jump]], [[https://github.com/jojojames/smart-jump][smart-jump]], [[https://github.com/iqbalansari/emacs-source-peek][source-peek]]
  - syntax checker :: flymake

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

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

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

* The beginning
  Every good file starts with a nice header and enable lexical scoping as per the
  recommendations of the [[https://github.com/bbatsov/emacs-lisp-style-guide#source-code-layout--organization][The Emacs Lisp Style Guide]].
  #+BEGIN_SRC emacs-lisp
  ;;; init.el --- Emacs Initialization -*- lexical-binding: t; -*-
  #+END_SRC

** Startup
   Time the startup.
   #+BEGIN_SRC emacs-lisp
   (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.
   #+BEGIN_SRC emacs-lisp
   (let ((normal-gc-cons-threshold 800000)
         (normal-gc-cons-percentage 0.1)
         (normal-file-name-handler-alist file-name-handler-alist)
         (init-gc-cons-threshold 402653184)
         (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

** Variables
   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
   (eval-and-compile
     (defvar +debug-mode
       (or (getenv "DEBUG") init-file-debug)
       "Debug mode, enable through DEBUG=1 or use --debug-init.")
     (setq debug-on-error (and (not noninteractive) +debug-mode)))
   #+END_SRC

   Provide a location where Emacs can store data and cache.
   #+BEGIN_SRC emacs-lisp
   (eval-and-compile
     (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."))
   #+END_SRC

** Quiet startup
   #+BEGIN_SRC emacs-lisp
   (setq inhibit-default-init t
         inhibit-startup-screen t
         initial-major-mode 'fundamental-mode
         initial-scratch-message nil)
   #+END_SRC

** Disable GUI components
   #+BEGIN_SRC emacs-lisp
   (when window-system
     (setq use-dialog-box nil)
     (menu-bar-mode 0)
     (scroll-bar-mode 0)
     (tool-bar-mode 0)
     (tooltip-mode 0))
   #+END_SRC

** UX
   Disable bell (both visual and audible)
   #+BEGIN_SRC emacs-lisp
   (setq ring-bell-function #'ignore
         visible-bell nil)
   #+END_SRC

   Use =y= / =n= instead of =yes= / =no=
   #+BEGIN_SRC emacs-lisp
   (setq confirm-kill-emacs #'y-or-n-p)
   (fset #'yes-or-no-p #'y-or-n-p)
   #+END_SRC

** Custom file
   Put custom defs in a temporary file so it doesn't grow over time.
   #+BEGIN_SRC emacs-lisp
   (setq custom-file
         (expand-file-name "custom.el" temporary-file-directory))
   #+END_SRC

** Emacs source
   Provide a location for the Emacs source in case I want to look up the
   function definitions of some of the C functions.
   #+BEGIN_SRC emacs-lisp
   (setq source-directory (expand-file-name "emacs" "~/src/git.sv.gnu.org"))
   #+END_SRC

** Load path
   #+BEGIN_SRC emacs-lisp
   (eval-and-compile
     (setq load-path
           (append (delete-dups load-path)
                   (list (expand-file-name "lisp" user-emacs-directory)))))
   #+END_SRC

** OS
*** macOS
    Setup PATH correctly.
    #+BEGIN_SRC emacs-lisp
    (when (eq system-type 'darwin)
      (setenv "PATH" (shell-command-to-string "launchctl getenv PATH")))
    #+END_SRC

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

** Package management
   Don't use defualt =package.el=:
   #+BEGIN_SRC emacs-lisp
   (setq package-enable-at-startup nil)
   #+END_SRC

*** straight.el
    Install =straight.el= to manage packages. Although it is horribly slow to
    initialize (epkgs I'm looking at you) it lets me avoid all the mess that is
    require to get an efficient startup with the built-in =package.el=.
    #+BEGIN_SRC emacs-lisp
    (defvar straight-recipes-gnu-elpa-use-mirror t)
    (eval-and-compile
      (defvar bootstrap-version)
      (let ((bootstrap-file
             (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
            (bootstrap-version 5))
        (unless (file-exists-p bootstrap-file)
          (with-current-buffer
              (url-retrieve-synchronously
               "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
               'silent 'inhibit-cookies)
            (goto-char (point-max))
            (eval-print-last-sexp)))
        (load bootstrap-file nil 'nomessage)))

    (setq straight-cache-autoloads t
          straight-use-package-by-default t
          straight-vc-git-default-protocol 'https
          straight-vc-git-force-protocol t)
    #+END_SRC

*** use-package
    #+BEGIN_SRC emacs-lisp
    (eval-and-compile
      (straight-use-package 'use-package)
      (require 'use-package)
      (setq use-package-always-defer t)
      (if +debug-mode
          (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

** Packages
*** no-littering
    Help keeping =~/.emacs.d= clean.
    #+BEGIN_SRC emacs-lisp
    (use-package no-littering
      :demand t
      :preface
      (defvar no-littering-etc-directory +data-dir)
      (defvar no-littering-var-directory +cache-dir))
    #+END_SRC

*** exec-path-from-shell
    #+BEGIN_SRC emacs-lisp
    (use-package exec-path-from-shell
      :demand t
      :custom
      (exec-path-from-shell-check-startup-files nil)
      :config
      (exec-path-from-shell-initialize))
    #+END_SRC

*** server
    #+BEGIN_SRC emacs-lisp
    (use-package server
      :straight nil
      :defer 3
      :config
      (unless (server-running-p)
        (server-start)))
    #+END_SRC

*** savehist
    Persist history.
    #+BEGIN_SRC emacs-lisp
    (use-package savehist
      :straight nil
      :hook
      (after-init . savehist-mode)
      :custom
      (savehist-additional-variables '(kill-ring
                                       search-ring
                                       regexp-search-ring))
      (savehist-autosave-interval 60)
      (savehist-save-minibuffer-history t))
    #+END_SRC

*** saveplace
    Keep track of last point place to resume editing in the same file.
    #+BEGIN_SRC emacs-lisp
    (use-package saveplace
      :straight nil
      :defer 5
      :config
      (save-place-mode 1))
    #+END_SRC

** Libraries
*** cl-lib
    #+BEGIN_SRC emacs-lisp
    (require 'cl-lib)
    #+END_SRC

*** async
    #+BEGIN_SRC emacs-lisp
    (use-package async
      :demand t)
    #+END_SRC

* Base
** Performance
   Disable bidirectional text for tiny performance boost
   #+BEGIN_SRC emacs-lisp
   (setq-default bidi-display-reordering nil)
   #+END_SRC

   Update UI less frequently
   #+BEGIN_SRC emacs-lisp
   (setq idle-update-delay 2
         jit-lock-defer-time 0
         jit-lock-stealth-time 0.2
         jit-lock-stealth-verbose nil)
   #+END_SRC

** Localization
   #+BEGIN_SRC emacs-lisp
   (setq-default calendar-week-start-day 1)
   #+END_SRC

** Terminal
*** Less
    Make less work inside Emacs shells.
    #+BEGIN_SRC emacs-lisp
    (setenv "LESS" "--dumb --prompt=s")
    #+END_SRC

** Packages
*** all-the-icons
    Support functions for displaying icons. I am trying to use icons where possible.
    #+BEGIN_SRC emacs-lisp
    (use-package all-the-icons
      :demand t
      :straight
      (:host github :repo "ubolonton/all-the-icons.el" :branch "font-lock-fix" :files (:defaults "data")))
    #+END_SRC

*** auto-minor-mode
    Enable minor modes by buffer name and contents. It provides the =use-package= keyword
    =:minor= and =:magic-minor= where you can specify these rules.
    #+BEGIN_SRC emacs-lisp
    (use-package auto-minor-mode :demand t)
    #+END_SRC

*** defrepeater
    Support defining repeating commands by repeating the last key.
    #+BEGIN_SRC emacs-lisp
    (use-package defrepeater)
    #+END_SRC

*** diminish
    Hide mode-line modes, more room for important stuff.
    #+BEGIN_SRC emacs-lisp
    (use-package diminish)
    #+END_SRC

*** general
    More convenient key definitions. I provides the =use-package= keyword =:general=.
    #+BEGIN_SRC emacs-lisp
    (use-package general :demand t)
    #+END_SRC

*** minions
    #+BEGIN_SRC emacs-lisp
    (use-package minions
      :general
      ([C-S-mouse-1] 'minions-minor-modes-menu))
    #+END_SRC

*** quick-peek
    Library to display inline popups; used by =source-peek=.
     #+BEGIN_SRC emacs-lisp
    (use-package quick-peek)
     #+END_SRC

*** which-key
    Display available keybindings in a popup.
    #+BEGIN_SRC emacs-lisp
    (use-package which-key
      :defer 1
      :diminish which-key-mode
      :custom
      (which-key-sort-order #'which-key-key-order-alpha)
      (which-key-sort-uppercase-first nil)
      (which-key-add-column-padding 1)
      (which-key-min-display-lines 5)
      (which-key-idle-delay 0.5)
      :config
      (push '(("<\\([[:alnum:]-]+\\)>" . nil) . ("\\1" . nil)) which-key-replacement-alist)
      (push '(("\\`\\?\\?\\'" . nil)          . ("λ" . nil)) which-key-replacement-alist)
      (push '(("<up>"    . nil)               . ("↑" . nil)) which-key-replacement-alist)
      (push '(("<right>" . nil)               . ("→" . nil)) which-key-replacement-alist)
      (push '(("<down>"  . nil)               . ("↓" . nil)) which-key-replacement-alist)
      (push '(("<left>"  . nil)               . ("←" . nil)) which-key-replacement-alist)
      (push '(("SPC" . nil)                   . ("␣" . nil)) which-key-replacement-alist)
      (push '(("TAB" . nil)                   . ("↹" . nil)) which-key-replacement-alist)
      (push '(("RET" . nil)                   . ("⏎" . nil)) which-key-replacement-alist)
      (push '(("DEL" . nil)                   . ("⌫" . nil)) which-key-replacement-alist)
      (push '(("deletechar" . nil)            . ("⌦" . nil)) which-key-replacement-alist)

      (which-key-add-key-based-replacements
        "C-c !" "check"
        "C-c @" "outline"
        "C-c &" "snippet"
        "C-c m" "major-mode"
        "C-c W" "workspace")
      (which-key-setup-side-window-bottom)

      (which-key-mode 1))
    #+END_SRC

** Functions
   Prevent forms from producing output or other noise.
   #+BEGIN_SRC emacs-lisp
   (defmacro quiet! (&rest forms)
     "Run FORMS without making any noise."
     `(if +debug-mode
          (progn ,@forms)
        (fset '+old-write-region-fn (symbol-function 'write-region))
        (cl-letf ((standard-output (lambda (&rest _)))
                  ((symbol-function 'load-file) (lambda (file) (load file nil t)))
                  ((symbol-function 'message) (lambda (&rest _)))
                  ((symbol-function 'write-region)
                   (lambda (start end filename &optional append visit lockname mustbenew)
                     (unless visit (setq visit 'no-message))
                     (when (fboundp '+old-write-region-fn)
                       (+old-write-region-fn
                        start end filename append visit lockname mustbenew))))
                  (inhibit-message t)
                  (save-silently t))
          ,@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.

   Example:
     (advice-add #'orig-fun :around #'quiet-function-advice)"
     (quiet! (apply orig-fn args)))
   #+END_SRC

*** Process
    #+BEGIN_SRC emacs-lisp
    (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

*** Utils
    #+BEGIN_SRC emacs-lisp
    (defun work-log-file ()
      "Open today's work log file."
      (interactive)
      (let ((daily-name (format-time-string "%Y-%m-%d")))
        (find-file (expand-file-name (concat "~/org/worklog-" daily-name ".org")))))

    (defun sprint-file ()
      "Open current sprint log file."
      (interactive)
      (let ((sprint-name (format-time-string "%Y-W%W")))
        (find-file (expand-file-name (concat "~/org/sprint-" sprint-name ".org")))))
    #+END_SRC

*** Window
    #+BEGIN_SRC emacs-lisp
    (defun toggle-dedicated-window ()
      (interactive)
      (set-window-dedicated-p (selected-window)
                              (not (window-dedicated-p (selected-window)))))
    #+END_SRC

* Appearance
** Typography
*** Size
    #+BEGIN_SRC emacs-lisp
    (eval-and-compile
      (defvar +line-spacing 0.25
        "Spacing between lines.")

      (defvar +default-font-height 140
        "Default font height."))
    #+END_SRC

*** Face
    #+BEGIN_SRC emacs-lisp
    (eval-and-compile
      (defvar +fixed-pitch-font "Iosevka T Slab"
        "Font used for fixed-pitch faces.")

      (defvar +variable-pitch-font "Noto Sans"
        "Font used for variable-pitch faces.")

      (defvar +serif-font "Noto Serif"
        "Font used for serif faces.")

      (defvar +unicode-font "Noto Mono"
        "Fallback font used for unicode glyphs."))
    #+END_SRC

*** Line length
    #+BEGIN_SRC emacs-lisp
    (setq-default fill-column 90)
    #+END_SRC

** Theme
*** tao-theme (tao-yang)
    An excellent, light, uncoloured theme.
    #+BEGIN_SRC emacs-lisp
    (use-package tao-theme
      :demand t
      :custom
      (tao-theme-use-height t)
      (tao-theme-use-sepia t))

    (load-theme 'tao-yang t)
    #+END_SRC

*** Local overrides
    No theme is perfect, also this is a good place to put my own experiments.
    #+BEGIN_SRC emacs-lisp
    (require 'local-theme)
    (load-theme 'local t)
    #+END_SRC

*** Echo area
    Adjust the font face used for the echo area.
    #+BEGIN_SRC emacs-lisp
    (dolist (buffer '(" *Echo Area 0*"
                      " *Echo Area 1*"))
      (with-current-buffer (get-buffer buffer)
        (setq-local face-remapping-alist '((default (:height 0.9) variable-pitch)))))
    #+END_SRC

** Layout
*** Padding
    Add some margins to make text feel less crowded. Put fringes on the outside for the
    same reason.
    #+BEGIN_SRC emacs-lisp
    (setq-default fringes-outside-margins t
                  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
    (when (boundp 'window-divider-mode)
      (setq window-divider-default-places t
            window-divider-default-bottom-width 1
            window-divider-default-right-width 1)
      (window-divider-mode 1))
    #+END_SRC

*** Frame
    Add some padding around the whole window (=internal-border-width=) to provide some air.
    Remove GUI elements as well as maximize the window; A maximized window ensures macOS
    won't waste extra space all-though it is technically not maximized. Make the title-bar
    transparent on macOS.
    #+BEGIN_SRC emacs-lisp
    (setq default-frame-alist
          '((internal-border-width . 12)
            (vertical-scroll-bars . nil)
            (menu-bar-lines . 0)
            (tool-bar-lines . 0)))

    (when (eq system-type 'darwin)
      (setq frame-title-format nil)
      (dolist (filter '((ns-transparent-titlebar . t)
                        (ns-appearance . unbound)))
        (push filter default-frame-alist)))
    #+END_SRC

** Mode line
   I am trying to keep the original mode line as much as possible to ensure compatibility
   with various packages and features, but I have rearranged the position of things and
   also introduced a right side mode line. I have defined a helper function to fill up the
   space needed to make the mode line appear on the right side.

*** Right alignment
    Setup the right aligned mode line and helper functions to display it.
    #+BEGIN_SRC emacs-lisp
    (defvar mode-line-space
      '(:propertize
        " " display
        ((space :width 1)))
      "Space between mode line components.")

    (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)
                               ,(+ 1
                                   (* (string-width formatted-line)
                                      0.8))))))
         formatted-line)))

    (setq-default mode-line-format
                  (append
                   mode-line-format
                   '((:eval (mode-line-right)))))
    #+END_SRC

    Move default components to the right side of the mode line.
    #+BEGIN_SRC emacs-lisp
    (delete 'mode-line-position mode-line-format)
    (delete 'mode-line-mule-info mode-line-format)
    (delete 'mode-line-modes mode-line-format)

    (setq mode-line-right-format
          `(,mode-line-position
            (:eval mode-line-mule-info)
            ,mode-line-space
            ,mode-line-modes))
    #+END_SRC

*** Position
    Add position information including column and line number but skip the percentage.
    #+BEGIN_SRC emacs-lisp
    (setq mode-line-percent-position nil)
    (column-number-mode 1)
    (line-number-mode 1)
    #+END_SRC

*** Indentation
    Display information about the current indentation settings.
    #+BEGIN_SRC emacs-lisp
    (use-package indent-info
      :defer 2
      :custom
      (indent-info-insert-target 'mode-line-mule-info)
      (indent-info-prefix " ")
      (indent-info-suffix " ")
      :config
      (global-indent-info-mode 1))
    #+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
    (use-package hide-mode-line)
    #+END_SRC

*** Diminished modes
    Hide minor modes to save space.
    #+BEGIN_SRC emacs-lisp
    (diminish 'abbrev-mode)
    (diminish 'auto-fill-function)
    (diminish 'eldoc-mode)
    (with-eval-after-load 'face-remap (diminish 'buffer-face-mode))
    (with-eval-after-load 'with-editor (diminish 'with-editor-mode))
    #+END_SRC

*** Icons
**** Modified
     #+BEGIN_SRC emacs-lisp
     (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) " "))))

     (with-eval-after-load 'all-the-icons
       (setq-default mode-line-modified '((:eval (mode-line-modified-icons)))))
     #+END_SRC

**** Remote
     #+BEGIN_SRC emacs-lisp
     (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) " ")))

     (with-eval-after-load 'all-the-icons
       (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
     (defun +shorten-vc-mode-line (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 #'+shorten-vc-mode-line)
     #+END_SRC

* Display
** Frame
   Don't implicitly resize frames when changes various settings.
   #+BEGIN_SRC emacs-lisp
   (setq frame-inhibit-implied-resize t)
   #+END_SRC

** Window
*** Favor horizontal splits
    #+BEGIN_SRC emacs-lisp
    (setq split-width-threshold nil)
    #+END_SRC

*** Manage layouts
    #+BEGIN_SRC emacs-lisp
    (use-package winner
      :straight nil
      :hook
      (window-setup . winner-mode)
      :preface
      (unless (fboundp 'winner-redo-repeat)
        (defrepeater 'winner-redo-repeat #'winner-redo))
      (unless (fboundp 'winner-undo-repeat)
        (defrepeater 'winner-undo-repeat #'winner-undo))
      :general
      ([remap winner-redo] #'winner-redo-repeat
       [remap winner-undo] #'winner-undo-repeat))
    #+END_SRC

*** Navigation
    #+BEGIN_SRC emacs-lisp
    (use-package ace-window
      :custom
      (aw-ignore-current t)
      (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
      (aw-scope 'frame)
      :general
      ([remap other-window] 'ace-window))
    #+END_SRC

*** Zoom
    Zoom a window to display as a single window temporarily.
    #+BEGIN_SRC emacs-lisp
    (use-package zoom-window
      :general
      ("C-x C-z" 'zoom-window-zoom))
    #+END_SRC

** Buffer
*** Hide async shell command buffers
    #+BEGIN_SRC emacs-lisp
    (push '("^*Async Shell Command*" . (display-buffer-no-window))
          display-buffer-alist)
    #+END_SRC

*** Popups
    Always display pop up buffers at the bottom and regard all star buffers as such
    buffers.
    #+BEGIN_SRC emacs-lisp
    (push `(,(rx bos "*" (one-or-more anything) "*" (optional "<" (one-or-more anything) ">") eos)
            (display-buffer-reuse-window
             display-buffer-in-side-window)
            (reusable-frames . visible)
            (side            . bottom)
            (window-height   . 0.4))
          display-buffer-alist)
    #+END_SRC

*** Visual Fill Column
    #+BEGIN_SRC emacs-lisp
    (use-package visual-fill-column
      :hook
      (nov-mode . visual-fill-column-mode)
      :custom
      (visual-fill-column-width fill-column))
    #+END_SRC

* Editing
** Indentation
   Default to indentation by spaces, that seems to be what most languages use nowadays.
   #+BEGIN_SRC emacs-lisp
   (setq-default indent-tabs-mode nil)
   #+END_SRC

   Convert between tabs and spaces (only tabify initial whitespace)
   #+BEGIN_SRC emacs-lisp
   (setq-default tabify-regexp "^\t* [ \t]+")
   #+END_SRC

** Text flow
   Wrap at words and don't require double spaces to end a sentence.
   #+BEGIN_SRC emacs-lisp
   (setq sentence-end-double-space nil)
   (setq-default word-wrap t)
   #+END_SRC

   Disable line wrapping.
   #+BEGIN_SRC emacs-lisp
   (setq-default truncate-lines t)
   #+END_SRC

** Kill-ring
   Save clipboard contents into kill-ring before replacing them
   #+BEGIN_SRC emacs-lisp
   (setq save-interprogram-paste-before-kill t)
   #+END_SRC

** Packages
*** avy
    Jump to things.
    #+BEGIN_SRC emacs-lisp
    (use-package avy
      :custom
      (avy-all-windows nil)
      :general
      ("M-g f" 'avy-goto-char-timer))
    #+END_SRC

*** goto-addr
    Buttonize URLs and e-mail addresses in the current buffer.
    #+BEGIN_SRC emacs-lisp
    (use-package goto-addr
      :straight nil
      :hook
      (text-mode . goto-address-mode)
      (prog-mode . goto-address-prog-mode))
    #+END_SRC

*** rotate-text
    Cycle through words, symbols and patterns.
    #+BEGIN_SRC emacs-lisp
    (use-package rotate-text
      :general
      (:keymaps
       'global
       "M-n" 'rotate-text
       "M-p" 'rotate-text-backward)
      :config
      (dolist (item '(("true" "false")
                      ("assert" "refute")
                      ("yes" "no")))
        (push item rotate-text-words)))
    #+END_SRC

*** smartparens
    Auto-insert matching parenthesis and highlight matching parenthesis.
    #+BEGIN_SRC emacs-lisp
    (use-package smartparens
      :diminish
      (smartparens-mode smartparens-strict-mode)
      :hook
      (after-init . smartparens-global-strict-mode)
      (after-init . show-smartparens-global-mode)
      (eval-expression-minibuffer-setup . smartparens-strict-mode)
      :general
      (:keymaps
       'smartparens-mode-map
       "M-D" 'sp-unwrap-sexp
       "M-R" 'sp-rewrap-sexp)
      :config
      (require 'smartparens-config)
      (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil))
    #+END_SRC

*** whitespace
    Display whitespace
    #+BEGIN_SRC emacs-lisp
    (use-package whitespace
      :straight nil
      :custom
      (whitespace-line-column fill-column)
      (whitespace-style
       '(face tabs tab-mark spaces space-mark trailing lines-tail))
      (whitespace-display-mappings
       '((tab-mark ?\t [?› ?\t])
         (newline-mark 10 [?¬ 10])
         (space-mark 32 [183] [46]))))
    #+END_SRC

*** whole-line-or-region
    Cut/copy (=C-w= / =M-w=) the current line if no region is active.
    #+BEGIN_SRC emacs-lisp
    (use-package whole-line-or-region
      :diminish whole-line-or-region-local-mode
      :hook
      (after-init . whole-line-or-region-global-mode))
    #+END_SRC

*** yasnippet
    #+BEGIN_SRC emacs-lisp
    (use-package yasnippet
      :diminish yas-minor-mode
      :hook
      (after-init . yas-global-mode)
      :custom
      (yas-also-auto-indent-first-line t)
      (yas-snippet-dirs (list (expand-file-name "snippets" user-emacs-directory)))
      ;; Nested snippets
      (yas-triggers-in-field t)
      (yas-wrap-around-region t)
      :general
      (:keymaps
       'yas-minor-mode-map
       [tab] 'nil
       "TAB" 'nil)
      :init
      (setq yas-verbosity 0))

    (use-package yasnippet-snippets
      :defer 2)
    #+END_SRC

*** yatemplate
    #+BEGIN_SRC emacs-lisp
    (use-package autoinsert
      :straight nil
      :defer 1
      :custom
      (auto-insert-query nil)
      (auto-insert-alist nil)
      :config
      (auto-insert-mode 1))

    (use-package yatemplate
      :hook
      (yas-global-mode . yatemplate-fill-alist))
    #+END_SRC

** Appearance
*** Page breaks
    Display page breaks as a horizontal line.
    #+BEGIN_SRC emacs-lisp
    (use-package page-break-lines
      :defer 1
      :diminish page-break-lines-mode
      :config
      (global-page-break-lines-mode 1))
    #+END_SRC

*** Line highlight
    #+BEGIN_SRC emacs-lisp
    (use-package hl-line
      :straight nil
      :hook
      ((prog-mode conf-mode) . hl-line-mode)
      :custom
      ;; Only highlight in selected window
      (hl-line-sticky-flag nil)
      (global-hl-line-sticky-flag nil))
    #+END_SRC

* Buffers
  Prevent generation of useless lock and backup files.
  #+BEGIN_SRC emacs-lisp
  (setq create-lockfiles nil
        make-backup-files nil)
  #+END_SRC

  Don't require confirmation when opening a new buffer.
  #+BEGIN_SRC emacs-lisp
  (setq confirm-nonexistent-file-or-buffer t)
  #+END_SRC

  Remove visual indicators from non-selected windows
  #+BEGIN_SRC emacs-lisp
  (setq highlight-nonselected-windows nil)
  (setq-default cursor-in-non-selected-windows nil)
  #+END_SRC

** Auto save
   #+BEGIN_SRC emacs-lisp
   (auto-save-visited-mode 1)
   (setq save-abbrevs 'silently)
   #+END_SRC

** Auto executable scripts
   #+BEGIN_SRC emacs-lisp
   (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
   #+END_SRC

** Fringe
   Display indicator for buffer boundaries in the right fringe.
   #+BEGIN_SRC emacs-lisp
   (setq-default indicate-buffer-boundaries 'right)
   #+END_SRC

** Minibuffer
   Enable recursive minibuffers and keep the point out of the minibuffer.
   #+BEGIN_SRC emacs-lisp
   (setq enable-recursive-minibuffers t
         minibuffer-prompt-properties
         '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt))
   #+END_SRC

   Give some more room to the minbuffer.
   #+BEGIN_SRC emacs-lisp
   (setq max-mini-window-height 0.3
         resize-mini-windows 'grow-only)
   #+END_SRC

*** No fringes
    Don't show fringes in the minibuffer.
    #+BEGIN_SRC emacs-lisp
    (defun +disable-minibuffer-window-fringes ()
      "Disable the window fringes for minibuffer window."
      (set-window-fringes (minibuffer-window) 0 0 nil))
    (add-hook 'emacs-startup-hook #'+disable-minibuffer-window-fringes)
    (add-hook 'minibuffer-setup-hook #'+disable-minibuffer-window-fringes)
    #+END_SRC

*** History
    Persist minibuffer history
    #+BEGIN_SRC emacs-lisp
    (setq history-delete-duplicates t
          history-length 500)
    #+END_SRC

*** Miniedit
    Edit minibuffer in a new temporary buffer by pressing =M-C-e=.
    #+BEGIN_SRC emacs-lisp
    (use-package miniedit
      :general
      (:keymaps
       '(minibuffer-local-map
         minibuffer-local-ns-map
         minibuffer-local-completion-map
         minibuffer-local-must-match-map)
       "M-C-e" 'miniedit))
    #+END_SRC

** Scrolling
   More procedural scrolling.
   #+BEGIN_SRC emacs-lisp
   (setq auto-window-vscroll nil
         hscroll-margin 5
         hscroll-step 5
         scroll-conservatively 101
         scroll-margin 0
         scroll-preserve-screen-position t)

   (setq-default scroll-down-aggressively 0.01
                 scroll-up-aggressively 0.01)
   #+END_SRC

** Text scale
   Text scaling works across all buffers. I rarely find that I only want to change the
   text scale only for one buffer.
   #+BEGIN_SRC emacs-lisp
   (defun +text-scale-increase-across-all-buffers (orig &rest args)
     "Text scale across all buffers.
   Calls ORIG function with ARGS."
     (dolist (buffer (buffer-list))
       (with-current-buffer buffer
         (apply orig args))))
   (advice-add 'text-scale-increase :around #'+text-scale-increase-across-all-buffers)
   #+END_SRC

*** default-text-scale
    #+BEGIN_SRC emacs-lisp
    (use-package default-text-scale
      :general
      (:keymaps
       'global
       "C-M-=" 'default-text-scale-increase
       "C-M--" 'default-text-scale-decrease
       "C-M-0" 'default-text-scale-reset))
    #+END_SRC

** Packages
*** autorevert
    Revert buffers when underlying files change.
    #+BEGIN_SRC emacs-lisp
    (use-package autorevert
      :straight nil
      :defer 2
      :diminish auto-revert-mode
      :custom
      (auto-revert-verbose nil)
      :config
      (global-auto-revert-mode 1))
    #+END_SRC

*** eldoc
    #+BEGIN_SRC emacs-lisp
    (use-package eldoc
      :straight nil
      :config
      (global-eldoc-mode 1))
    #+END_SRC

*** rainbow-mode
    Display colors inline.
    #+BEGIN_SRC emacs-lisp
    (use-package rainbow-mode
      :minor
      "-theme\\.el$")
    #+END_SRC

*** readable
    #+BEGIN_SRC emacs-lisp
    (use-package readable
      :straight nil
      :diminish readable-mode
      :hook
      ((eww-mode
        markdown-mode
        nov-mode
        org-mode) . readable-mode))
    #+END_SRC

*** spray
    #+BEGIN_SRC emacs-lisp
    (use-package spray
      :custom
      (spray-height 500)
      (spray-margin-left 2)
      :config
{'fraction_non_alphanumeric': 0.12942428812206155, 'fraction_numerical': 0.0047162837458233535, 'mean_word_length': 2.3365726124704027, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 19, 'https://': 32, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1125915', 'n_tokens_mistral': 23466, 'n_tokens_neox': 22399, 'n_words': 5458}