Human Readable Emacs Configuration using Org mode
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

62 KiB

Emacs Configuration

Summary

I've really been wanting to have a nicely formatted emacs config file and this is my attempt at it.

Required Magic

Lexical Binding

  ;;; -*- lexical-binding: t -*-
  ;;; DO NOT EDIT THIS FILE DIRECTLY
  ;;; EDIT ~init.org~ instead

The Magical Glue

The following auto compiles the emacs-lisp within the init.org file. Simply run `org-babel-tangle` to make it RAIN!

  ;; (setq byte-compile-warnings nil)
  (defun tangle-init ()
    "If the current buffer is 'init.org' the code-blocks are tangled, and the tangled file is compiled."
    (when (equal (buffer-file-name)
                 (expand-file-name (concat user-emacs-directory "init.org")))
      ;; Avoid running hooks when tangling.
      (let ((prog-mode-hook nil))
        (org-babel-tangle)
        (byte-compile-file (concat user-emacs-directory "init.el")))))

  (add-hook 'after-save-hook 'tangle-init)

Config

Packages

  (require 'package)

  (defvar my-packages
    '(all-the-icons
      anzu
      base16-theme
      better-defaults
      company
      company-go
      counsel
      counsel-projectile
      dash-at-point
      diminish
      dockerfile-mode
      doom-themes
      ein
      eldoc-eval
      elpy
      expand-region
      fic-mode
      gitignore-mode
      go-mode
      go-playground
      gorepl-mode
      flycheck
      iedit
      ivy
      ivy-hydra
      json-mode
      magit
      material-theme
      multiple-cursors
      projectile
      py-autopep8
      rainbow-delimiters
      shrink-path
      tide
      typescript-mode
      use-package
      web-mode
      which-key))

  (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
  (add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/"))

  (when (not package-archive-contents)
    (package-refresh-contents))
  (package-initialize)

  (dolist (p my-packages)
    (when (not (package-installed-p p))
      (package-install p)))

Better Defaults

  (require 'better-defaults)

  ;; Instead of the annoying giant warning icon, just flash the modeline.
  ;; (this happens when you do something like C-g)
  (setq ring-bell-function
        (lambda ()
          (let ((orig-fg (face-foreground 'mode-line)))
            (set-face-foreground 'mode-line "#F2804F")
            (run-with-idle-timer 0.1 nil
                                 (lambda (fg) (set-face-foreground 'mode-line fg))
                                 orig-fg))))

  (defun set-frame-size-according-to-resolution ()
    "Set the Emacs window size on startup."
    (interactive)
    (if window-system
        (progn
          ;; WIDTH
          (if (> (x-display-pixel-width) 1280)
              ;; Large Screen (only show 120 cols)
              (add-to-list 'default-frame-alist (cons 'width 240))
            ;; Small Screen (fill window)
            (add-to-list 'default-frame-alist (cons 'width (/ (x-display-pixel-width) (frame-char-width)))))

          ;; HEIGHT
          (if (> (x-display-pixel-height) 1080)
              ;; Large Screen (only fill half screen)
              (add-to-list 'default-frame-alist (cons 'height (/ (/ (x-display-pixel-height) 2)
                                                                 (frame-char-height))))
            ;; Small Screen (fill window)
            (add-to-list 'default-frame-alist (cons 'height (/ (x-display-pixel-height) (frame-char-height)))))
          )))

  (set-frame-size-according-to-resolution)

  (defun window-px-width ()
    "Get the width of the Emacs window in pixels."
    (interactive)
    (* (* (window-total-width) 2.874) (frame-char-width)))

  (defun window-px-left-pos ()
    "Calculate the left position of the Emacs window."
    (interactive)
    (/ (- (x-display-pixel-width) (window-px-width)) 2))


  (add-to-list 'default-frame-alist (cons 'top 0))
  (add-to-list 'default-frame-alist (cons 'left 1000))

Splash Screen

       (setq inhibit-splash-screen nil
             fancy-splash-image "~/.emacs.d/public/emacs-logo.png"
             fancy-splash-image-file "~/.emacs.d/public/emacs-logo.png")

Basic Customization

  (defvar backup-dir (expand-file-name "~/.emacs.d/backup/"))
  (defvar autosave-dir (expand-file-name "~/.emacs.d/autosave/"))

  (setq initial-scratch-message nil
        backup-directory-alist (list (cons ".*" backup-dir))
        auto-save-list-file-prefix autosave-dir
        auto-save-file-name-transforms `((".*" ,autosave-dir t)))

  (menu-bar-mode 0)
  (scroll-bar-mode 0)
  (tool-bar-mode 0)


  ;; (load-theme 'doom-city-lights t)
  ;; (load-theme 'doom-dracula t)
  ;; (load-theme 'doom-nord t)
  (load-theme 'doom-one t)
  ;; (load-theme 'doom-spacegrey t)
  ;; (load-theme 'base16-ocean t)
  (load-theme 'base16-onedark t)
  (global-linum-mode t)
  (global-auto-revert-mode t)

  (defalias 'yes-or-no-p 'y-or-n-p)

Tools

General

  (require 'which-key)
  (which-key-setup-minibuffer)
  (which-key-mode)

  (require 'fic-mode)
  (add-hook 'js-mode-hook 'fic-mode)

Company

  (require 'company)
  (add-hook 'after-init-hook 'global-company-mode)

Diminish

  (require 'diminish)
  (diminish 'auto-revert-mode)
  (eval-after-load "company" '(diminish 'company-mode))
  (eval-after-load "counsel" '(diminish 'counsel-mode))
  (eval-after-load "elpy" '(diminish 'elpy-mode))
  (eval-after-load "go-mode" '(diminish 'go-mode))
  (eval-after-load "go-playground" '(diminish 'go-playground-mode))
  (eval-after-load "gorepl-mode" '(diminish 'gorepl-mode))
  (eval-after-load "flycheck" '(diminish 'flycheck-mode))
  (eval-after-load "ivy" '(diminish 'ivy-mode))
  (eval-after-load "projectile" '(diminish 'projectile-mode))
  (eval-after-load "which-key" '(diminish 'which-key-mode))

Dired

  (defun dired-mode-setup ()
    "Will run as hook for `dired-mode'."
    (dired-hide-details-mode 1))
  (add-hook 'dired-mode-hook 'dired-mode-setup)

Ivy

  (require 'ivy-hydra)
  (require 'ivy)
  (require 'swiper)

  (ivy-mode 1)
  (counsel-mode)
  (setq ivy-use-virtual-buffers t
        enable-recursive-minibuffers t
        ivy-height 25
        ivy-initial-inputs-alist nil
        ivy-extra-directories nil)

  (global-set-key (kbd "C-s") 'swiper)
  (global-set-key (kbd "C-c C-r") 'ivy-resume)
  (global-set-key (kbd "M-x") 'counsel-M-x)
  (global-set-key (kbd "C-x C-f") 'counsel-find-file)
  (global-set-key (kbd "C-c g") 'counsel-git)
  (global-set-key (kbd "C-c j") 'counsel-git-grep)
  (global-set-key (kbd "C-c k") 'counsel-ag)
  (define-key minibuffer-local-map (kbd "C-r") 'counsel-minibuffer-history)

  (defun ivy-open-current-typed-path ()
    (interactive)
    (when ivy--directory
      (let* ((dir ivy--directory)
             (text-typed ivy-text)
             (path (concat dir text-typed)))
        (delete-minibuffer-contents)
        (ivy--done path))))

  (define-key ivy-minibuffer-map (kbd "<return>") 'ivy-alt-done)
  (define-key ivy-minibuffer-map (kbd "C-f") 'ivy-open-current-typed-path)

Magit

  (require 'magit)
  (global-set-key (kbd "C-x g") 'magit-status)
  (global-set-key (kbd "C-c g") 'magit-status)
  (setq magit-completing-read-function 'ivy-completing-read)

Projectile

  (require 'projectile)
  (require 'counsel-projectile)

  (projectile-mode)
  (setq projectile-mode-line '(:eval (format " %s" (projectile-project-name)))
        projectile-remember-window-configs t
        projectile-completion-system 'ivy)
  (counsel-projectile-mode)

Development Specific

General

  (require 'rainbow-delimiters)
  (global-flycheck-mode)

  (add-hook 'before-save-hook 'delete-trailing-whitespace)
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode)

  (setq-default indent-tabs-mode nil
                tab-width 4)
  (defvaralias 'c-basic-offset 'tab-width)
  (defvaralias 'cperl-indent-level 'tab-width)

  (electric-pair-mode 1)
  (show-paren-mode 1)

  (require 'dockerfile-mode)
  (add-to-list 'auto-mode-alist '("Dockerfile*\\'" . dockerfile-mode))

  (require 'gitignore-mode)
  (add-to-list 'auto-mode-alist '("gitignore\\'" . gitignore-mode))

  (require 'json-mode)
  (add-to-list 'auto-mode-alist '("\\.json\\'" . json-mode))

  (require 'web-mode)
  (add-to-list 'auto-mode-alist '("\\.html\\'" . web-mode))

Python

  (elpy-enable)
  (setq python-shell-interpreter "jupyter"
        python-shell-interpreter-args "console --simple-prompt")

  (when (require 'flycheck nil t)
    (setq elpy-modules (delq 'elpy-module-flymake elpy-modules))
    (add-hook 'elpy-mode-hook 'flycheck-mode))

  (require 'py-autopep8)
  (setq py-autopep8-options '("--ignore=E501"))
  (add-hook 'elpy-mode-hook 'py-autopep8-enable-on-save)

Go

  (require 'go-mode)
  (require 'go-playground)
  (require 'gorepl-mode)
  (require 'company-go)

  (add-to-list 'auto-mode-alist '("\\.go\\'" . go-mode))
  (add-hook 'go-mode-hook (lambda ()
                            (add-hook 'before-save-hook 'gofmt-before-save)
                            (local-set-key (kbd "M-.") 'godef-jump)
                            (local-set-key (kbd "M-,") 'pop-tag-mark)
                            (local-set-key (kbd "C-c C-c") (lambda ()
                                                             (interactive)
                                                             (ansi-term)
                                                             (comint-send-string "*ansi-term*" "make\n")))
                            (set (make-local-variable 'company-backends) '(company-go))
                            (setq company-tooltip-limit 20
                                  company-idle-delay .3
                                  company-echo-delay 0
                                  company-begin-commands '(self-insert-command))
                            (gorepl-mode)))
  (defun set-exec-path-from-shell-PATH ()
    (let ((path-from-shell (replace-regexp-in-string
                            "[ \t\n]*$"
                            ""
                            (shell-command-to-string "$SHELL --login -i -c 'echo $PATH'"))))
      (setenv "PATH" path-from-shell)
      (setq eshell-path-env path-from-shell)
      (setq exec-path (split-string path-from-shell path-separator))))

  (when window-system (set-exec-path-from-shell-PATH))

  (setenv "GOPATH" "/Users/leviolson/go")
  (add-to-list 'exec-path "/Users/leviolson/go/bin")

TypeScript

  (defun setup-tide-mode ()
    "Tide setup function."
    (interactive)
    (tide-setup)
    (flycheck-mode +1)
    (setq flycheck-check-syntax-automatically '(save mode-enabled))
    (eldoc-mode +1)
    (tide-hl-identifier-mode +1)
    (company-mode +1))

  ;; aligns annotation to the right hand side
  (setq company-tooltip-align-annotations t)

  ;; formats the buffer before saving
  (add-hook 'before-save-hook 'tide-format-before-save)

  (add-hook 'typescript-mode-hook #'setup-tide-mode)

  (require 'typescript-mode)
  (require 'tide)

  (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode))
  (add-hook 'typescript-mode-hook
            '(lambda ()
               (set (make-local-variable 'company-backends) '(company-tide))
               (setq company-tooltip-limit 20
                     company-idle-delay .3
                     company-echo-delay 0
                     company-begin-commands '(self-insert-command)
                     tide-format-options '(:insertSpaceAfterFunctionKeywordForAnonymousFunctions t :placeOpenBraceOnNewLineForFunctions nil))
               (tide-setup)))
TSX
  (require 'web-mode)
  (add-to-list 'auto-mode-alist '("\\.tsx\\'" . web-mode))
  (add-hook 'web-mode-hook
            (lambda ()
              (when (string-equal "tsx" (file-name-extension buffer-file-name))
                (setup-tide-mode))))
  ;; enable typescript-tslint checker
  (flycheck-add-mode 'typescript-tslint 'web-mode)
JSX
  (require 'web-mode)
  (add-to-list 'auto-mode-alist '("\\.jsx\\'" . web-mode))
  (add-hook 'web-mode-hook
            (lambda ()
              (when (string-equal "jsx" (file-name-extension buffer-file-name))
                (setup-tide-mode))))
  ;; configure jsx-tide checker to run after your default jsx checker
  (flycheck-add-mode 'javascript-eslint 'web-mode)
  (flycheck-add-next-checker 'javascript-eslint 'jsx-tide 'append)

Org

  (org-babel-do-load-languages
   'org-babel-load-languages
   '((js . t)
     (shell . t)
     (emacs-lisp . t)))

  (defvar org-src-tab-acts-natively)
  (setq org-src-tab-acts-natively t)
  ;; (setenv "NODE_PATH"
  ;;          (getenv "NODE_PATH"))

  (defvar org-confirm-babel-evaluate)

  (defun my-org-confirm-babel-evaluate (lang body)
    "Execute certain languages without confirming.
        Takes LANG to allow and BODY to execute."
    (not (or (string= lang "js")
             (string= lang "restclient")
             (string= lang "emacs-lisp")
             (string= lang "shell"))))
  (setq org-confirm-babel-evaluate #'my-org-confirm-babel-evaluate)
  (add-to-list 'org-structure-template-alist
               (list "e" (concat "#+BEGIN_SRC emacs-lisp :results silent\n"
                                 "\n"
                                 "#+END_SRC")))
  (add-to-list 'org-structure-template-alist
               (list "j" (concat "#+BEGIN_SRC js :cmd \"babel-node\"\n"
                                 "\n"
                                 "#+END_SRC")))
  (add-to-list 'org-structure-template-alist
               (list "r" (concat "#+BEGIN_SRC restclient :results raw\n"
                                 "\n"
                                 "#+END_SRC")))

Functions

  (defun find-user-init-file ()
    "Edit the `~/.emacs.d/init.org' file."
    (interactive)
    (find-file "~/.emacs.d/init.org"))

  (defun load-user-init-file ()
    "LO: Reload the `~/.emacs.d/init.elc' file."
    (interactive)
    (load-file "~/.emacs.d/init.elc"))

  (defun jump-to-symbol-internal (&optional backwardp)
    "Jumps to the next symbol near the point if such a symbol exists.  If BACKWARDP is non-nil it jumps backward."
    (let* ((point (point))
           (bounds (find-tag-default-bounds))
           (beg (car bounds)) (end (cdr bounds))
           (str (isearch-symbol-regexp (find-tag-default)))
           (search (if backwardp 'search-backward-regexp
                     'search-forward-regexp)))
      (goto-char (if backwardp beg end))
      (funcall search str nil t)
      (cond ((<= beg (point) end) (goto-char point))
            (backwardp (forward-char (- point beg)))
            (t  (backward-char (- end point))))))

  (defun jump-to-previous-like-this ()
    "Jumps to the previous occurrence of the symbol at point."
    (interactive)
    (jump-to-symbol-internal t))

  (defun jump-to-next-like-this ()
    "Jumps to the next occurrence of the symbol at point."
    (interactive)
    (jump-to-symbol-internal))

  (defun match-paren (arg)
    "Go to the matching paren if on a paren; otherwise insert ARG (a literal % sign)."
    (interactive "p")
    (cond ((looking-at "\\s(") (forward-list 1))
          ((looking-back "\\s(" 2) (backward-char 1) (forward-list 1))
          ((looking-at "\\s)") (forward-char 1) (backward-list 1))
          ((looking-back "\\s)" 2) (backward-list 1))
          (t (self-insert-command (or arg 1)))))

  (defun kill-this-buffer-unless-scratch ()
    "Works like `kill-this-buffer' unless the current buffer is the *scratch* buffer.  In which case the buffer content is deleted and the buffer is buried."
    (interactive)
    (if (not (string= (buffer-name) "*scratch*"))
        (kill-this-buffer)
      (delete-region (point-min) (point-max))
      (switch-to-buffer (other-buffer))
      (bury-buffer "*scratch*")))

  (defun delete-backward-sentence ()
    "LO: Delete to the beginning of the sentence/line."
    (interactive)
    (delete-region (point) (progn (backward-sentence) (point))))

  (defun delete-backward-to-boundary (arg)
    "LO: Delete backward to the previous word boundary.  With ARG, do this many times."
    (interactive "p")
    (let ((a (point))
          (b (progn
               (backward-word arg)
               (forward-word)
               (point))))
      (if (< a b)
          (delete-region a (progn (backward-word arg) (point)))
        (if (= a b)
            (delete-region a (progn (backward-word arg) (point)))
          (delete-region a b)))))

  (defun comment-or-uncomment-region-or-line ()
    "Comments or uncomments the region or the current line if there's no active region."
    (interactive)
    (let (beg end)
      (if (region-active-p)
          (setq beg (region-beginning) end (region-end))
        (setq beg (line-beginning-position) end (line-end-position)))
      (comment-or-uncomment-region beg end)))

  (defun fold-toggle (column)
    "Code folding by COLUMN."
    (interactive "P")
    (set-selective-display
     (or column
         (unless selective-display
           (1+ (current-column))))))

  (defun new-line-below ()
    "LO: Create a new line below current line."
    (interactive)
    (move-end-of-line 1)
    (newline-and-indent))

  (defun new-line-above ()
    "LO: Create a new line above current line."
    (interactive)
    (move-beginning-of-line 1)
    (newline)
    (forward-line -1))

  (defun duplicate-thing (comment)
    "LO: Duplicates the current line, or the region if active.  If an argument (COMMENT) is given, the duplicated region will be commented out."
    (interactive "P")
    (save-excursion
      (let ((start (if (region-active-p) (region-beginning) (point-at-bol)))
            (end   (if (region-active-p) (region-end) (point-at-eol))))
        (goto-char end)
        (unless (region-active-p)
          (newline))
        (insert (buffer-substring start end))
        (when comment (comment-region start end)))))

  (defun tidy ()
    "LO: Ident, untabify and unwhitespacify current buffer, or region if active."
    (interactive)
    (let ((beg (if (region-active-p) (region-beginning) (point-min)))
          (end (if (region-active-p) (region-end) (point-max))))
      (let ((inhibit-message t))
        (indent-region beg end))
      (whitespace-cleanup)
      (untabify beg (if (< end (point-max)) end (point-max)))
      (if (region-active-p) (message "Indenting Region...Done") (message "Indenting File...Done"))))

  (defun phil-columns ()
    "LO: Good 'ol Phil-Columns."
    (interactive)
    (message "Good 'ol fill-columns")
    (with-output-to-temp-buffer "*PHIL-COLUMN*"
      (shell-command "mpv --no-video 'https://www.youtube.com/watch?v=YkADj0TPrJA&t=3m16s' > /dev/null 2>&1 & sleep 8; pkill mpv"))
    (other-window 1)
    (delete-window))

  (declare-function first "Goto FIRST shell.")
  (declare-function goto-non-shell-buffer "Goto something other than a shell buffer.")
  (declare-function switch-shell "Switch shell.")

  (let ((last-shell ""))
    (defun toggle-shell ()
      (interactive)
      (cond ((string-match-p "^\\*shell<[1-9][0-9]*>\\*$" (buffer-name))
             (goto-non-shell-buffer))
            ((get-buffer last-shell) (switch-to-buffer last-shell))
            (t (shell (setq last-shell "*shell<1>*")))))

    (defun switch-shell (n)
      (let ((buffer-name (format "*shell<%d>*" n)))
        (setq last-shell buffer-name)
        (cond ((get-buffer buffer-name)
               (switch-to-buffer buffer-name))
              (t (shell buffer-name)
                 (rename-buffer buffer-name)))))

    (defun goto-non-shell-buffer ()
      (let* ((r "^\\*shell<[1-9][0-9]*>\\*$")
             (shell-buffer-p (lambda (b) (string-match-p r (buffer-name b))))
             (non-shells (cl-remove-if shell-buffer-p (buffer-list))))
        (when non-shells
          (switch-to-buffer (first non-shells))))))


  (defadvice shell (after kill-with-no-query nil activate)
    "."
    (set-process-query-on-exit-flag (get-buffer-process ad-return-value) nil))

  (declare-function comint-truncate-buffer ".")
  (defun clear-comint ()
    "Run `comint-truncate-buffer' with the `comint-buffer-maximum-size' set to zero."
    (interactive)
    (let ((comint-buffer-maximum-size 0))
      (comint-truncate-buffer)))

  (defun c-setup ()
    "Compile."
    (local-set-key (kbd "C-c C-c") 'compile))

Bindings

  (require 'company)
  (add-hook 'comint-mode-hook (lambda () (local-set-key (kbd "C-l") 'clear-comint)))
  (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
  (add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode)
  (add-hook 'c-mode-common-hook 'c-setup)
  (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))

  (defvar company-active-map (make-keymap)
    "Company Mode keymap.")
  (defvar custom-bindings (make-keymap)
    "A keymap of custom bindings.")

  (define-key global-map          (kbd "M-p")          'jump-to-previous-like-this)
  (define-key global-map          (kbd "M-n")          'jump-to-next-like-this)
  (define-key global-map          (kbd "M-<tab>")      'switch-to-next-buffer)
  (define-key global-map          (kbd "M-<backspace>")'delete-backward-to-boundary)
  (define-key global-map          (kbd "C-<backspace>")'delete-backward-to-boundary)

  (global-set-key                 (kbd "C-S-<down>")   'mc/mark-next-like-this)
  (global-set-key                 (kbd "C->")          'mc/mark-next-like-this)
  (global-set-key                 (kbd "C-S-<up>")     'mc/mark-previous-like-this)
  (global-set-key                 (kbd "C-<")          'mc/mark-previous-like-this)
  (global-set-key                 (kbd "C-c C->")      'mc/mark-all-like-this)
  (global-set-key                 "%"                  'match-paren)
  (global-set-key                 (kbd "C-x .")        'dash-at-point)
  (global-set-key                 (kbd "C-x ,")        'dash-at-point-with-docset)
  (global-set-key                 (kbd "C-s")          (lambda () (interactive) (swiper (format "%s" (thing-at-point 'symbol)))))

  ;; (dolist (n (number-sequence 1 9))
  ;;   (global-set-key (kbd (concat "M-" (int-to-string n)))
  ;;                   (lambda () (interactive) (switch-shell n))))

  (define-key company-active-map  (kbd "C-d")          'company-show-doc-buffer)
  (define-key company-active-map  (kbd "C-n")          'company-select-next)
  (define-key company-active-map  (kbd "C-p")          'company-select-previous)
  (define-key company-active-map  (kbd "<tab>")        'company-complete)

  (define-key custom-bindings     (kbd "C-c p")        'counsel-projectile-switch-project)
  (define-key custom-bindings     (kbd "C-c f")        'counsel-projectile-find-file)
  (define-key custom-bindings     (kbd "C-c m")        'magit-status)
  (define-key custom-bindings     (kbd "C-c D")        'define-word-at-point)
  (define-key custom-bindings     (kbd "C-@")          'er/expand-region)
  (define-key custom-bindings     (kbd "C-#")          'er/contract-region)
  (define-key custom-bindings     (kbd "C-S-c C-S-c")  'mc/edit-lines)
  (define-key custom-bindings     (kbd "C-c b")        'ivy-switch-buffer)
  (define-key custom-bindings     (kbd "C-c l")        'org-store-link)
  (define-key custom-bindings     (kbd "C-c t")        'org-set-tags)
  (define-key custom-bindings     (kbd "M-u")          'upcase-dwim)
  (define-key custom-bindings     (kbd "M-c")          'capitalize-dwim)
  (define-key custom-bindings     (kbd "M-l")          'downcase-dwim)
  (define-key custom-bindings     (kbd "M-o")          'other-window)
  (define-key custom-bindings     (kbd "C-c s")        'ispell-word)
  (define-key custom-bindings     (kbd "C-c C-d")      'org-capture)
  (define-key custom-bindings     (kbd "C-c <up>")     'windmove-up)
  (define-key custom-bindings     (kbd "C-c <down>")   'windmove-down)
  (define-key custom-bindings     (kbd "C-c <left>")   'windmove-left)
  (define-key custom-bindings     (kbd "C-c <right>")  'windmove-right)
  (define-key custom-bindings     (kbd "C-c a")        (lambda () (interactive) (org-agenda nil "n")))
  (define-key custom-bindings     (kbd "C-c e")        'find-user-init-file)
  (define-key custom-bindings     (kbd "C-x f")        'phil-columns)
  (define-key custom-bindings     (kbd "C-x k")        'kill-this-buffer-unless-scratch)
  (define-key custom-bindings     (kbd "C-c d")        'duplicate-thing)
  (define-key custom-bindings     (kbd "C-c c")        'comment-or-uncomment-region-or-line)
  (define-key custom-bindings     (kbd "C-;")          'comment-or-uncomment-region-or-line)
  (define-key custom-bindings     (kbd "C-o")          'new-line-below)
  (define-key custom-bindings     (kbd "C-S-o")        'new-line-above)
  (define-key custom-bindings     (kbd "<C-tab>")      'tidy)
  (define-key custom-bindings     (kbd "M-q")          'kill-this-buffer)
  (define-key custom-bindings     (kbd "M-RET")        '(lambda () (interactive) (term (getenv "SHELL"))))


  (define-minor-mode custom-bindings-mode
    "A mode that activates custom-bindings."
    t nil custom-bindings)

UI

  (cond ((member "PragmataPro" (font-family-list))
         (set-face-attribute 'default nil :font "PragmataPro-14")))

Modeline

  (require 'use-package)
  (require 'anzu)
  (require 'eldoc-eval)
  (require 'iedit)
  (require 'projectile)
  (require 'all-the-icons)

  (defsubst doom--prepare-modeline-segments (segments)
    (cl-loop for seg in segments
             if (stringp seg)
             collect seg
             else
             collect (list (intern (format "doom-modeline-segment--%s" (symbol-name seg))))))

  (defvar doom--transient-counter 0)
  (defmacro add-transient-hook! (hook &rest forms)
    "Attaches transient forms to a HOOK.

  HOOK can be a quoted hook or a sharp-quoted function (which will be advised).

  These forms will be evaluated once when that function/hook is first invoked,
  then it detaches itself."
    (declare (indent 1))
    (let ((append (eq (car forms) :after))
          (fn (intern (format "doom-transient-hook-%s" (cl-incf doom--transient-counter)))))
      `(when ,hook
         (fset ',fn
               (lambda (&rest _)
                 ,@forms
                 (cond ((functionp ,hook) (advice-remove ,hook #',fn))
                       ((symbolp ,hook)   (remove-hook ,hook #',fn)))
                 (unintern ',fn nil)))
         (cond ((functionp ,hook)
                (advice-add ,hook ,(if append :after :before) #',fn))
               ((symbolp ,hook)
                (add-hook ,hook #',fn ,append))))))


  (defmacro add-hook! (&rest args)
    "A convenience macro for `add-hook'. Takes, in order:
    1. Optional properties :local and/or :append, which will make the hook
       buffer-local or append to the list of hooks (respectively),
    2. The hooks: either an unquoted major mode, an unquoted list of major-modes,
       a quoted hook variable or a quoted list of hook variables. If unquoted, the
       hooks will be resolved by appending -hook to each symbol.
    3. A function, list of functions, or body forms to be wrapped in a lambda.
  Examples:
      (add-hook! 'some-mode-hook 'enable-something)
      (add-hook! some-mode '(enable-something and-another))
      (add-hook! '(one-mode-hook second-mode-hook) 'enable-something)
      (add-hook! (one-mode second-mode) 'enable-something)
      (add-hook! :append (one-mode second-mode) 'enable-something)
      (add-hook! :local (one-mode second-mode) 'enable-something)
      (add-hook! (one-mode second-mode) (setq v 5) (setq a 2))
      (add-hook! :append :local (one-mode second-mode) (setq v 5) (setq a 2))
  Body forms can access the hook's arguments through the let-bound variable
  `args'."
    (declare (indent defun) (debug t))
    (let ((hook-fn 'add-hook)
          append-p local-p)
      (while (keywordp (car args))
        (pcase (pop args)
          (:append (setq append-p t))
          (:local  (setq local-p t))
          (:remove (setq hook-fn 'remove-hook))))
      (let ((hooks (doom--resolve-hook-forms (pop args)))
            (funcs
             (let ((val (car args)))
               (if (memq (car-safe val) '(quote function))
                   (if (cdr-safe (cadr val))
                       (cadr val)
                     (list (cadr val)))
                 (list args))))
            forms)
        (dolist (fn funcs)
          (setq fn (if (symbolp fn)
                       `(function ,fn)
                     `(lambda (&rest _) ,@args)))
          (dolist (hook hooks)
            (push (if (eq hook-fn 'remove-hook)
                      `(remove-hook ',hook ,fn ,local-p)
                    `(add-hook ',hook ,fn ,append-p ,local-p))
                  forms)))
        `(progn ,@(nreverse forms)))))

  (defmacro def-modeline-segment! (name &rest forms)
    "Defines a modeline segment and byte compiles it."
    (declare (indent defun) (doc-string 2))
    (let ((sym (intern (format "doom-modeline-segment--%s" name))))
      `(progn
         (defun ,sym () ,@forms)
         ,(unless (bound-and-true-p byte-compile-current-file)
            `(let (byte-compile-warnings)
               (byte-compile #',sym))))))

  (defmacro def-modeline! (name lhs &optional rhs)
    "Defines a modeline format and byte-compiles it. NAME is a symbol to identify
  it (used by `doom-modeline' for retrieval). LHS and RHS are lists of symbols of
  modeline segments defined with `def-modeline-segment!'.
  Example:
    (def-modeline! minimal
      (bar matches \" \" buffer-info)
      (media-info major-mode))
    (doom-set-modeline 'minimal t)"
    (let ((sym (intern (format "doom-modeline-format--%s" name)))
          (lhs-forms (doom--prepare-modeline-segments lhs))
          (rhs-forms (doom--prepare-modeline-segments rhs)))
      `(progn
         (defun ,sym ()
           (let ((lhs (list ,@lhs-forms))
                 (rhs (list ,@rhs-forms)))
             (let ((rhs-str (format-mode-line rhs)))
               (list lhs
                     (propertize
                      " " 'display
                      `((space :align-to (- (+ right right-fringe right-margin)
                                            ,(+ 1 (string-width rhs-str))))))
                     rhs-str))))
         ,(unless (bound-and-true-p byte-compile-current-file)
            `(let (byte-compile-warnings)
               (byte-compile #',sym))))))

  (defun doom-modeline (key)
    "Returns a mode-line configuration associated with KEY (a symbol). Throws an
  error if it doesn't exist."
    (let ((fn (intern (format "doom-modeline-format--%s" key))))
      (when (functionp fn)
        `(:eval (,fn)))))

  (defun doom-set-modeline (key &optional default)
    "Set the modeline format. Does nothing if the modeline KEY doesn't exist. If
  DEFAULT is non-nil, set the default mode-line for all buffers."
    (when-let ((modeline (doom-modeline key)))
      (setf (if default
                (default-value 'mode-line-format)
              (buffer-local-value 'mode-line-format (current-buffer)))
            modeline)))

  (use-package eldoc-eval
    :config
    (defun +doom-modeline-eldoc (text)
      (concat (when (display-graphic-p)
                (+doom-modeline--make-xpm
                 (face-background 'doom-modeline-eldoc-bar nil t)
                 +doom-modeline-height
                 +doom-modeline-bar-width))
              text))

    ;; Show eldoc in the mode-line with `eval-expression'
    (defun +doom-modeline--show-eldoc (input)
      "Display string STR in the mode-line next to minibuffer."
      (with-current-buffer (eldoc-current-buffer)
        (let* ((str              (and (stringp input) input))
               (mode-line-format (or (and str (or (+doom-modeline-eldoc str) str))
                                     mode-line-format))
               mode-line-in-non-selected-windows)
          (force-mode-line-update)
          (sit-for eldoc-show-in-mode-line-delay))))
    (setq eldoc-in-minibuffer-show-fn #'+doom-modeline--show-eldoc)

    (eldoc-in-minibuffer-mode +1))

  ;; anzu and evil-anzu expose current/total state that can be displayed in the
  ;; mode-line.
  (use-package anzu
    :init
    ;; (add-transient-hook! #'ex-start-search (require 'anzu))
    ;; (add-transient-hook! #'ex-start-word-search (require 'anzu))
    :config
    (setq anzu-cons-mode-line-p nil
          anzu-minimum-input-length 1
          anzu-search-threshold 250)
    ;; Avoid anzu conflicts across buffers
    (mapc #'make-variable-buffer-local
          '(anzu--total-matched anzu--current-position anzu--state
                                anzu--cached-count anzu--cached-positions anzu--last-command
                                anzu--last-isearch-string anzu--overflow-p))
    ;; Ensure anzu state is cleared when searches & iedit are done
    (add-hook 'isearch-mode-end-hook #'anzu--reset-status t)
    ;; (add-hook '+evil-esc-hook #'anzu--reset-status t)
    (add-hook 'iedit-mode-end-hook #'anzu--reset-status))


  ;; Keep `+doom-modeline-current-window' up-to-date
  (defvar +doom-modeline-current-window (frame-selected-window))
  (defun +doom-modeline|set-selected-window (&rest _)
    "Sets `+doom-modeline-current-window' appropriately"
    (when-let ((win (frame-selected-window)))
      (unless (minibuffer-window-active-p win)
        (setq +doom-modeline-current-window win))))

  (add-hook 'window-configuration-change-hook #'+doom-modeline|set-selected-window)
  (add-hook 'focus-in-hook #'+doom-modeline|set-selected-window)
  (advice-add #'handle-switch-frame :after #'+doom-modeline|set-selected-window)
  (advice-add #'select-window :after #'+doom-modeline|set-selected-window)

  ;; fish-style modeline
  (use-package shrink-path
    :commands (shrink-path-prompt shrink-path-file-mixed))


  ;;
  ;; Variables
  ;;

  (defvar +doom-modeline-height 29
    "How tall the mode-line should be (only respected in GUI emacs).")

  (defvar +doom-modeline-bar-width 3
    "How wide the mode-line bar should be (only respected in GUI emacs).")

  (defvar +doom-modeline-vspc
    (propertize " " 'face 'variable-pitch)
    "TODO")

  (defvar +doom-modeline-buffer-file-name-style 'truncate-upto-project
    "Determines the style used by `+doom-modeline-buffer-file-name'.

  Given ~/Projects/FOSS/emacs/lisp/comint.el
  truncate-upto-project => ~/P/F/emacs/lisp/comint.el
  truncate-upto-root => ~/P/F/e/lisp/comint.el
  truncate-all => ~/P/F/e/l/comint.el
  relative-from-project => emacs/lisp/comint.el
  relative-to-project => lisp/comint.el
  file-name => comint.el")

  ;; externs
  (defvar anzu--state nil)
  (defvar evil-mode nil)
  (defvar evil-state nil)
  (defvar evil-visual-selection nil)
  (defvar iedit-mode nil)
  (defvar all-the-icons-scale-factor)
  (defvar all-the-icons-default-adjust)


  ;;
  ;; Custom faces
  ;;

  (defgroup +doom-modeline nil
    ""
    :group 'doom)

  (defface doom-modeline-buffer-path
    '((t (:inherit (mode-line-emphasis bold))))
    "Face used for the dirname part of the buffer path."
    :group '+doom-modeline)

  (defface doom-modeline-buffer-file
    '((t (:inherit (mode-line-buffer-id bold))))
    "Face used for the filename part of the mode-line buffer path."
    :group '+doom-modeline)

  (defface doom-modeline-buffer-modified
    '((t (:inherit (error bold) :background nil)))
    "Face used for the 'unsaved' symbol in the mode-line."
    :group '+doom-modeline)

  (defface doom-modeline-buffer-major-mode
    '((t (:inherit (mode-line-emphasis bold))))
    "Face used for the major-mode segment in the mode-line."
    :group '+doom-modeline)

  (defface doom-modeline-highlight
    '((t (:inherit mode-line-emphasis)))
    "Face for bright segments of the mode-line."
    :group '+doom-modeline)

  (defface doom-modeline-panel
    '((t (:inherit mode-line-highlight)))
    "Face for 'X out of Y' segments, such as `+doom-modeline--anzu', `+doom-modeline--evil-substitute' and
  `iedit'"
    :group '+doom-modeline)

  (defface doom-modeline-info
    `((t (:inherit (success bold))))
    "Face for info-level messages in the modeline. Used by `*vc'."
    :group '+doom-modeline)

  (defface doom-modeline-warning
    `((t (:inherit (warning bold))))
    "Face for warnings in the modeline. Used by `*flycheck'"
    :group '+doom-modeline)

  (defface doom-modeline-urgent
    `((t (:inherit (error bold))))
    "Face for errors in the modeline. Used by `*flycheck'"
    :group '+doom-modeline)

  ;; Bar
  (defface doom-modeline-bar '((t (:inherit highlight)))
    "The face used for the left-most bar on the mode-line of an active window."
    :group '+doom-modeline)

  (defface doom-modeline-eldoc-bar '((t (:inherit shadow)))
    "The face used for the left-most bar on the mode-line when eldoc-eval is
  active."
    :group '+doom-modeline)

  (defface doom-modeline-inactive-bar '((t (:inherit warning :inverse-video t)))
    "The face used for the left-most bar on the mode-line of an inactive window."
    :group '+doom-modeline)


  ;;
  ;; Modeline helpers
  ;;

  (defsubst active ()
    (eq (selected-window) +doom-modeline-current-window))

  ;; Inspired from `powerline's `pl/make-xpm'.
  (defun +doom-modeline--make-xpm (color height width)
    "Create an XPM bitmap."
    (propertize
     " " 'display
     (let ((data (make-list height (make-list width 1)))
           (color (or color "None")))
       (create-image
        (concat
         (format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\"  c %s\","
                 (length (car data))
                 (length data)
                 color
                 color)
         (apply #'concat
                (cl-loop with idx = 0
                         with len = (length data)
                         for dl in data
                         do (cl-incf idx)
                         collect
                         (concat "\""
                                 (cl-loop for d in dl
                                          if (= d 0) collect (string-to-char " ")
                                          else collect (string-to-char "."))
                                 (if (eq idx len) "\"};" "\",\n")))))
        'xpm t :ascent 'center))))

  (defun +doom-modeline-buffer-file-name ()
    "Propertized `buffer-file-name' based on `+doom-modeline-buffer-file-name-style'."
    (propertize
     (pcase +doom-modeline-buffer-file-name-style
       ('truncate-upto-project (+doom-modeline--buffer-file-name 'shrink))
       ('truncate-upto-root (+doom-modeline--buffer-file-name-truncate))
       ('truncate-all (+doom-modeline--buffer-file-name-truncate t))
       ('relative-to-project (+doom-modeline--buffer-file-name-relative))
       ('relative-from-project (+doom-modeline--buffer-file-name-relative 'include-project))
       ('file-name (propertize (file-name-nondirectory buffer-file-name)
                               'face
                               (let ((face (or (and (buffer-modified-p)
                                                    'doom-modeline-buffer-modified)
                                               (and (active)
                                                    'doom-modeline-buffer-file))))
                                 (when face `(:inherit ,face))))))
     'help-echo buffer-file-truename))

  (defun +doom-modeline--buffer-file-name-truncate (&optional truncate-tail)
    "Propertized `buffer-file-name' that truncates every dir along path.
  If TRUNCATE-TAIL is t also truncate the parent directory of the file."
    (let ((dirs (shrink-path-prompt (file-name-directory buffer-file-truename)))
          (active (active)))
      (if (null dirs)
          (propertize "%b" 'face (if active 'doom-modeline-buffer-file))
        (let ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)))
          (let ((dirname (car dirs))
                (basename (cdr dirs))
                (dir-faces (or modified-faces (if active 'doom-modeline-project-root-dir)))
                (file-faces (or modified-faces (if active 'doom-modeline-buffer-file))))
            (concat (propertize (concat dirname
                                        (if truncate-tail (substring basename 0 1) basename)
                                        "/")
                                'face (if dir-faces `(:inherit ,dir-faces)))
                    (propertize (file-name-nondirectory buffer-file-name)
                                'face (if file-faces `(:inherit ,file-faces)))))))))

  (defun +doom-modeline--buffer-file-name-relative (&optional include-project)
    "Propertized `buffer-file-name' showing directories relative to project's root only."
    (let ((root (projectile-project-root))
          (active (active)))
      (if (null root)
          (propertize "%b" 'face (if active 'doom-modeline-buffer-file))
        (let* ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified))
               (relative-dirs (file-relative-name (file-name-directory buffer-file-truename)
                                                  (if include-project (concat root "../") root)))
               (relative-faces (or modified-faces (if active 'doom-modeline-buffer-path)))
               (file-faces (or modified-faces (if active 'doom-modeline-buffer-file))))
          (if (equal "./" relative-dirs) (setq relative-dirs ""))
          (concat (propertize relative-dirs 'face (if relative-faces `(:inherit ,relative-faces)))
                  (propertize (file-name-nondirectory buffer-file-truename)
                              'face (if file-faces `(:inherit ,file-faces))))))))

  (defun +doom-modeline--buffer-file-name (truncate-project-root-parent)
    "Propertized `buffer-file-name'.
  If TRUNCATE-PROJECT-ROOT-PARENT is t space will be saved by truncating it down
  fish-shell style.

  Example:
  ~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el"
    (let* ((project-root (projectile-project-root))
           (file-name-split (shrink-path-file-mixed project-root
                                                    (file-name-directory buffer-file-truename)
                                                    buffer-file-truename))
           (active (active)))
      (if (null file-name-split)
          (propertize "%b" 'face (if active 'doom-modeline-buffer-file))
        (pcase-let ((`(,root-path-parent ,project ,relative-path ,filename) file-name-split))
          (let ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)))
            (let ((sp-faces       (or modified-faces (if active 'font-lock-comment-face)))
                  (project-faces  (or modified-faces (if active 'font-lock-string-face)))
                  (relative-faces (or modified-faces (if active 'doom-modeline-buffer-path)))
                  (file-faces     (or modified-faces (if active 'doom-modeline-buffer-file))))
              (let ((sp-props       `(,@(if sp-faces       `(:inherit ,sp-faces))      ,@(if active '(:weight bold))))
                    (project-props  `(,@(if project-faces  `(:inherit ,project-faces)) ,@(if active '(:weight bold))))
                    (relative-props `(,@(if relative-faces `(:inherit ,relative-faces))))
                    (file-props     `(,@(if file-faces     `(:inherit ,file-faces)))))
                (concat (propertize (if truncate-project-root-parent
                                        root-path-parent
                                      (abbreviate-file-name project-root))
                                    'face sp-props)
                        (propertize (concat project "/") 'face project-props)
                        (if relative-path (propertize relative-path 'face relative-props))
                        (propertize filename 'face file-props)))))))))


  ;;
  ;; Segments
  ;;

  (def-modeline-segment! buffer-default-directory
    "Displays `default-directory'. This is for special buffers like the scratch
  buffer where knowing the current project directory is important."
    (let ((face (if (active) 'doom-modeline-buffer-path)))
      (concat (if (display-graphic-p) " ")
              (all-the-icons-octicon
               "file-directory"
               :face face
               :v-adjust -0.05
               :height 1.25)
              (propertize (concat " " (abbreviate-file-name default-directory))
                          'face face))))

  ;;
  (def-modeline-segment! buffer-info
    "Combined information about the current buffer, including the current working
  directory, the file name, and its state (modified, read-only or non-existent)."
    (concat (cond (buffer-read-only
                   (concat (all-the-icons-octicon
                            "lock"
                            :face 'doom-modeline-warning
                            :v-adjust -0.05)
                           " "))
                  ((buffer-modified-p)
                   (concat (all-the-icons-faicon
                            "floppy-o"
                            :face 'doom-modeline-buffer-modified
                            :v-adjust -0.0575)
                           " "))
                  ((and buffer-file-name
                        (not (file-exists-p buffer-file-name)))
                   (concat (all-the-icons-octicon
                            "circle-slash"
                            :face 'doom-modeline-urgent
                            :v-adjust -0.05)
                           " "))
                  ((buffer-narrowed-p)
                   (concat (all-the-icons-octicon
                            "fold"
                            :face 'doom-modeline-warning
                            :v-adjust -0.05)
                           " ")))
            (if buffer-file-name
                (+doom-modeline-buffer-file-name)
              "%b")))

  ;;
  (def-modeline-segment! buffer-info-simple
    "Display only the current buffer's name, but with fontification."
    (propertize
     "%b"
     'face (cond ((and buffer-file-name (buffer-modified-p))
                  'doom-modeline-buffer-modified)
                 ((active) 'doom-modeline-buffer-file))))

  ;;
  (def-modeline-segment! buffer-encoding
    "Displays the encoding and eol style of the buffer the same way Atom does."
    (concat (pcase (coding-system-eol-type buffer-file-coding-system)
              (0 "LF  ")
              (1 "CRLF  ")
              (2 "CR  "))
            (let ((sys (coding-system-plist buffer-file-coding-system)))
              (cond ((memq (plist-get sys :category) '(coding-category-undecided coding-category-utf-8))
                     "UTF-8")
                    (t (upcase (symbol-name (plist-get sys :name))))))
            "  "))

  ;;
  (def-modeline-segment! major-mode
    "The major mode, including process, environment and text-scale info."
    (propertize
     (concat (format-mode-line mode-name)
             (when (stringp mode-line-process)
               mode-line-process)
             (and (featurep 'face-remap)
                  (/= text-scale-mode-amount 0)
                  (format " (%+d)" text-scale-mode-amount)))
     'face (if (active) 'doom-modeline-buffer-major-mode)))

  ;;
  (def-modeline-segment! vcs
    "Displays the current branch, colored based on its state."
    (when (and vc-mode buffer-file-name)
      (let* ((backend (vc-backend buffer-file-name))
             (state   (vc-state buffer-file-name backend)))
        (let ((face    'mode-line-inactive)
              (active  (active))
              (all-the-icons-default-adjust -0.1))
          (concat "  "
                  (cond ((memq state '(edited added))
                         (if active (setq face 'doom-modeline-info))
                         (all-the-icons-octicon
                          "git-compare"
                          :face face
                          :v-adjust -0.05))
                        ((eq state 'needs-merge)
                         (if active (setq face 'doom-modeline-info))
                         (all-the-icons-octicon "git-merge" :face face))
                        ((eq state 'needs-update)
                         (if active (setq face 'doom-modeline-warning))
                         (all-the-icons-octicon "arrow-down" :face face))
                        ((memq state '(removed conflict unregistered))
                         (if active (setq face 'doom-modeline-urgent))
                         (all-the-icons-octicon "alert" :face face))
                        (t
                         (if active (setq face 'font-lock-doc-face))
                         (all-the-icons-octicon
                          "git-compare"
                          :face face
                          :v-adjust -0.05)))
                  " "
                  (propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2))
                              'face (if active face))
                  " ")))))

  ;;
  (defun +doom-ml-icon (icon &optional text face voffset)
    "Displays an octicon ICON with FACE, followed by TEXT. Uses
  `all-the-icons-octicon' to fetch the icon."
    (concat (if vc-mode " " "  ")
            (when icon
              (concat
               (all-the-icons-material icon :face face :height 1.1 :v-adjust (or voffset -0.2))
               (if text +doom-modeline-vspc)))
            (when text
              (propertize text 'face face))
            (if vc-mode "  " " ")))

  (def-modeline-segment! flycheck
    "Displays color-coded flycheck error status in the current buffer with pretty
  icons."
    (when (boundp 'flycheck-last-status-change)
      (pcase flycheck-last-status-change
        ('finished (if flycheck-current-errors
                       (let-alist (flycheck-count-errors flycheck-current-errors)
                         (let ((sum (+ (or .error 0) (or .warning 0))))
                           (+doom-ml-icon "do_not_disturb_alt"
                                          (number-to-string sum)
                                          (if .error 'doom-modeline-urgent 'doom-modeline-warning)
                                          -0.25)))
                     (+doom-ml-icon "check" nil 'doom-modeline-info)))
        ('running     (+doom-ml-icon "access_time" nil 'font-lock-doc-face -0.25))
        ('no-checker  (+doom-ml-icon "sim_card_alert" "-" 'font-lock-doc-face))
        ('errored     (+doom-ml-icon "sim_card_alert" "Error" 'doom-modeline-urgent))
        ('interrupted (+doom-ml-icon "pause" "Interrupted" 'font-lock-doc-face)))))
  ;; ('interrupted (+doom-ml-icon "x" "Interrupted" 'font-lock-doc-face)))))

  ;;
  (defsubst doom-column (pos)
    (save-excursion (goto-char pos)
                    (current-column)))

  (def-modeline-segment! selection-info
    "Information about the current selection, such as how many characters and
  lines are selected, or the NxM dimensions of a block selection."
    (when (and (active) (or mark-active (eq evil-state 'visual)))
      (let ((reg-beg (region-beginning))
            (reg-end (region-end)))
        (propertize
         (let ((lines (count-lines reg-beg (min (1+ reg-end) (point-max)))))
           (cond ((or (bound-and-true-p rectangle-mark-mode)
                      (eq 'block evil-visual-selection))
                  (let ((cols (abs (- (doom-column reg-end)
                                      (doom-column reg-beg)))))
                    (format "%dx%dB" lines cols)))
                 ((eq 'line evil-visual-selection)
                  (format "%dL" lines))
                 ((> lines 1)
                  (format "%dC %dL" (- (1+ reg-end) reg-beg) lines))
                 (t
                  (format "%dC" (- (1+ reg-end) reg-beg)))))
         'face 'doom-modeline-highlight))))


  ;;
  (defun +doom-modeline--macro-recording ()
    "Display current Emacs or evil macro being recorded."
    (when (and (active) (or defining-kbd-macro executing-kbd-macro))
      (let ((sep (propertize " " 'face 'doom-modeline-panel)))
        (concat sep
                (propertize (if (bound-and-true-p evil-this-macro)
                                (char-to-string evil-this-macro)
                              "Macro")
                            'face 'doom-modeline-panel)
                sep
                (all-the-icons-octicon "triangle-right"
                                       :face 'doom-modeline-panel
                                       :v-adjust -0.05)
                sep))))

  (defsubst +doom-modeline--anzu ()
    "Show the match index and total number thereof. Requires `anzu', also
  `evil-anzu' if using `evil-mode' for compatibility with `evil-search'."
    (when (and anzu--state (not iedit-mode))
      (propertize
       (let ((here anzu--current-position)
             (total anzu--total-matched))
         (cond ((eq anzu--state 'replace-query)
                (format " %d replace " total))
               ((eq anzu--state 'replace)
                (format " %d/%d " here total))
               (anzu--overflow-p
                (format " %s+ " total))
               (t
                (format " %s/%d " here total))))
       'face (if (active) 'doom-modeline-panel))))

  (defsubst +doom-modeline--evil-substitute ()
    "Show number of matches for evil-ex substitutions and highlights in real time."
    (when (and evil-mode
               (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist)
                   (assq 'evil-ex-global-match evil-ex-active-highlights-alist)
                   (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist)))
      (propertize
       (let ((range (if evil-ex-range
                        (cons (car evil-ex-range) (cadr evil-ex-range))
                      (cons (line-beginning-position) (line-end-position))))
             (pattern (car-safe (evil-delimited-arguments evil-ex-argument 2))))
         (if pattern
             (format " %s matches " (how-many pattern (car range) (cdr range)))
           " - "))
       'face (if (active) 'doom-modeline-panel))))

  (defun doom-themes--overlay-sort (a b)
    (< (overlay-start a) (overlay-start b)))

  (defsubst +doom-modeline--iedit ()
    "Show the number of iedit regions matches + what match you're on."
    (when (and iedit-mode iedit-occurrences-overlays)
      (propertize
       (let ((this-oc (or (let ((inhibit-message t))
                            (iedit-find-current-occurrence-overlay))
                          (progn (iedit-prev-occurrence)
                                 (iedit-find-current-occurrence-overlay))))
             (length (length iedit-occurrences-overlays)))
         (format " %s/%d "
                 (if this-oc
                     (- length
                        (length (memq this-oc (sort (append iedit-occurrences-overlays nil)
                                                    #'doom-themes--overlay-sort)))
                        -1)
                   "-")
                 length))
       'face (if (active) 'doom-modeline-panel))))

  (def-modeline-segment! matches
    "Displays: 1. the currently recording macro, 2. A current/total for the
  current search term (with anzu), 3. The number of substitutions being conducted
  with `evil-ex-substitute', and/or 4. The number of active `iedit' regions."
    (let ((meta (concat (+doom-modeline--macro-recording)
                        (+doom-modeline--anzu)
                        (+doom-modeline--evil-substitute)
                        (+doom-modeline--iedit))))
      (or (and (not (equal meta "")) meta)
          (if buffer-file-name " %I "))))

  ;; TODO Include other information
  (def-modeline-segment! media-info
    "Metadata regarding the current file, such as dimensions for images."
    (cond ((eq major-mode 'image-mode)
           (cl-destructuring-bind (width . height)
               (image-size (image-get-display-property) :pixels)
             (format "  %dx%d  " width height)))))

  (def-modeline-segment! bar
    "The bar regulates the height of the mode-line in GUI Emacs.
  Returns \"\" to not break --no-window-system."
    (if (display-graphic-p)
        (+doom-modeline--make-xpm
         (face-background (if (active)
                              'doom-modeline-bar
                            'doom-modeline-inactive-bar)
                          nil t)
         +doom-modeline-height
         +doom-modeline-bar-width)
      ""))


  ;;
  ;; Mode lines
  ;;

  (def-modeline! main
    (bar matches " " buffer-info "  %l:%c %p  " selection-info)
    (buffer-encoding major-mode vcs flycheck))

  (def-modeline! minimal
    (bar matches " " buffer-info)
    (media-info major-mode))

  (def-modeline! special
    (bar matches " " buffer-info-simple "  %l:%c %p  " selection-info)
    (buffer-encoding major-mode flycheck))

  (def-modeline! project
    (bar buffer-default-directory)
    (major-mode))

  (def-modeline! media
    (bar " %b  ")
    (media-info major-mode))


  ;;
  ;; Hooks
  ;;

  (defun +doom-modeline|init ()
    "Set the default modeline."
    (doom-set-modeline 'main t)

    ;; This scratch buffer is already created and doesn't get a modeline. For the
    ;; love of Emacs, someone give the man a modeline!
    (with-current-buffer "*scratch*"
      (doom-set-modeline 'main)))

  (defun +doom-modeline|set-special-modeline ()
    (doom-set-modeline 'special))

  (defun +doom-modeline|set-media-modeline ()
    (doom-set-modeline 'media))

  (defun +doom-modeline|set-project-modeline ()
    (doom-set-modeline 'project))


  ;;
  ;; Bootstrap
  ;;

  (add-hook 'emacs-startup-hook #'+doom-modeline|init)
  ;; (add-hook 'doom-scratch-buffer-hook #'+doom-modeline|set-special-modeline)
  ;; (add-hook '+doom-dashboard-mode-hook #'+doom-modeline|set-project-modeline)

  (add-hook 'image-mode-hook   #'+doom-modeline|set-media-modeline)
  (add-hook 'org-src-mode-hook #'+doom-modeline|set-special-modeline)
  (add-hook 'circe-mode-hook   #'+doom-modeline|set-special-modeline)