My personal Emacs configuration
Find a file
2024-08-06 17:48:00 +01:00
banners Add a collection of emacs banners for dashboard 2024-03-01 07:40:33 +00:00
lib Experimenting with gnus-icalendar functions for ics2org.el 2024-07-16 18:03:47 +01:00
.gitignore Initial move config from init.el to block in README.org 2024-08-06 07:09:41 +01:00
bbdb.gpg Add some BBDB entries 2024-07-15 10:11:25 +01:00
diary Move mu4e config into README.org 2024-08-06 07:29:30 +01:00
init.el Initial move config from init.el to block in README.org 2024-08-06 07:09:41 +01:00
iosevka-theme.el Add font size to custom theme 2024-06-20 16:22:01 +01:00
README.org org-agenda don't start with log mode by default 2024-08-06 17:48:00 +01:00
templates Add missing newline in template 2024-08-06 16:31:41 +01:00

Emacs Config

Personal Emacs configuration. Clone to ~/.config/emacs/ (or ~/.emacs.d/) and install specified plugins.

Config

Customise use-package first, configuration must be set before first time it's used.

  ;; Configure packages archives with priority
  (setopt use-package-check-before-init t
          use-package-enable-imenu-support t)

  (use-package package
    :custom
    (package-archive-priorities '(("melpa" . 4) ("stable" . 3) ("nongnu" . 2) ("gnu" . 1)))
    (package-selected-packages
     '(
       ;; UI
       base16-theme doom-modeline nerd-icons nerd-icons-completion
       nerd-icons-corfu nerd-icons-dired nerd-icons-ibuffer
       ligature which-key diff-hl

       ;; Completion
       vertico orderless marginalia cape corfu corfu-terminal
       consult consult-eglot flyspell-correct tempel

       ;; IDE
       treesit-auto magit forge apheleia envrc rainbow-delimiters
       flymake-shellcheck flymake-yamllint flymake-clippy
       flymake-eslint markdown-mode pandoc-mode python-docstring
       nix-mode lua-mode

       ;; Org + LaTeX
       org-roam org-noter citar auctex htmlize

       ;; Other
       password-store emms bbdb ement scad-mode

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

Add custom function to ensure required packages are installed and updated.

  (defun my/package-ensure ()
    "Ensure packages are installed and updated."
    (interactive)
    (require 'use-package)
    (package-refresh-contents)
    (package-install-selected-packages)
    (package-autoremove)
    (package-upgrade-all))

Load custom.el if file exists in default location.

  (setq custom-file (locate-user-emacs-file "custom.el"))
  (when (and custom-file (file-exists-p custom-file))
    (load custom-file nil 'nomessage))

Defaults

Set some useful defaults. Some of these should be moved to relevant section of config.

  (setq user-full-name "Evie Litherland-Smith"
        user-mail-address "evie@xenia.me.uk"
        use-short-answers t
        kill-do-not-save-duplicates t)

  (set-default-coding-systems 'utf-8)
  (global-auto-revert-mode +1)
  (delete-selection-mode +1)

  ;; Bind normal forward/back buttons on mouse to next/previous buffer respectively
  (keymap-global-set "<mouse-8>" #'previous-buffer)
  (keymap-global-set "<mouse-9>" #'next-buffer)

UI and Appearance

Configure the look and feel of Emacs

  (setq inhibit-splash-screen t
        initial-frame-alist nil
        default-frame-alist nil)

  (setq-default truncate-lines t
                truncate-partial-width-windows nil)

  (setopt indent-tabs-mode nil
          async-shell-command-display-buffer nil
          compilation-scroll-output t)

  (global-prettify-symbols-mode +1)
  (which-function-mode +1)
  (tool-bar-mode -1)
  (scroll-bar-mode -1)

Modeline

  (use-package doom-modeline
    :hook (after-init . (lambda () (doom-modeline-mode +1)))
    :custom
    (doom-modeline-checker-simple-format nil)
    (doom-modeline-enable-word-count t)
    (doom-modeline-env-version t)
    (doom-modeline-github nil)
    (doom-modeline-gnus t)
    (doom-modeline-mu4e t)
    (doom-modeline-icon t)
    (doom-modeline-irc t)
    (doom-modeline-irc-buffers t)
    (doom-modeline-lsp t)
    (doom-modeline-project-detection 'project)
    (doom-modeline-continuous-word-count-modes '(org-mode
                                                 markdown-mode
                                                 gfm-mode))
    :config
    (set-face-attribute 'doom-modeline nil :weight 'normal))

Completion

  (setq tab-always-indent 'complete
        completion-cycle-threshold nil
        completions-detailed t)

Org Mode

Email and Messaging

MU4E

Configure email with iCalendar event support, to integrate with org-agenda.

  (use-package sendmail
    :custom
    (sendmail-program (executable-find "msmtp"))
    (send-mail-function #'sendmail-send-it))

  (use-package message
    :custom
    (message-send-mail-function #'message-send-mail-with-sendmail)
    (message-sendmail-f-is-evil t)
    (message-sendmail-extra-arguments '("--read-envelope-from"))
    (message-auto-save-directory nil)
    (message-kill-buffer-on-exit t))

  (setq mail-user-agent 'mu4e-user-agent
        read-mail-command 'mu4e)

  (use-package mm-decode
    :custom
    (mm-discouraged-alternatives '("text/html")))

  (use-package mu4e
    :if (package-installed-p 'mu4e)
    :bind
    (("C-c m" . mu4e)
     :map mu4e-view-mode-map
     ("o n" . mu4e-org-store-and-capture))
    :custom
    (mu4e-read-option-use-builtin nil)
    (mu4e-completing-read-function #'completing-read)
    (mu4e-split-view 'horizontal)
    (mu4e-attachment-dir "~/Downloads")
    (mu4e-get-mail-command "mbsync -a")
    (mu4e-update-interval (* 5 60)) ; Every 5 minutes
    (mu4e-headers-auto-update nil)
    (mu4e-sent-messages-behavior 'sent)
    (mu4e-change-filenames-when-moving t)
    (mu4e-context-policy 'pick-first)
    (mu4e-compose-context-policy 'ask)
    (mu4e-compose-signature-auto-include nil)
    (mu4e-compose-complete-only-personal nil)
    (mu4e-eldoc-support t)
    (mu4e-search-full nil)
    (mu4e-search-include-related t)
    (mu4e-search-threads t)
    (mu4e-search-skip-duplicates t)
    (mu4e-maildir-shortcuts '((:maildir "/Proton/Inbox/" :key ?p)
                              (:maildir "/iCloud/Inbox/" :key ?i)
                              (:maildir "/Outlook/Inbox/" :key ?w)))
    (mu4e-bookmarks '((:name "Inbox" :query "maildir:/inbox/" :key ?i :favorite t)
                      (:name "Today" :query "date:today..now AND maildir:/inbox/" :key ?t)
                      (:name "Drafts" :query "flag:draft AND NOT flag:trashed" :key ?d :hide-unread t)
                      (:name "Unread" :query "flag:unread AND maildir:/inbox/" :key ?u :hide-unread t)
                      (:name "Flagged" :query "flag:flagged AND NOT flag:trashed" :key ?f :hide-unread t)
                      (:name "Spam" :query "maildir:/spam/ OR maildir:/junk/" :key ?s :hide-unread t)))
    (mu4e-headers-visible-lines 3)
    (mu4e-headers-fields
     '((:human-date . 8)
       (:flags . 10)
       (:from . 22)
       (:subject)))
    (mu4e-headers-visible-flags
     '(draft
       flagged
       unread
       passed
       replied
       trashed
       attach
       calendar
       encrypted
       signed
       list
       personal))
    :config
    (setq mu4e-use-fancy-chars t)

    )

  (with-eval-after-load 'mu4e
    (require 'mu4e-context)
    (setq mu4e-contexts
          (list
           (make-mu4e-context
            :name "Personal"
            :match-func (lambda (msg) (when msg (string-prefix-p "/Proton" (mu4e-message-field msg :maildir))))
            :vars '(
                    (user-mail-address . "e.litherlandsmith@proton.me")
                    (mu4e-sent-folder . "/Proton/Sent")
                    (mu4e-trash-folder . "/Proton/Trash")
                    (mu4e-refile-folder . "/Proton/Archive")
                    (message-cite-style . message-cite-style-thunderbird)
                    (message-signature . (concat "Evelyn Litherland-Smith (she/they)\n"))
                    ))
           (make-mu4e-context
            :name "Alternate"
            :match-func (lambda (msg) (when msg (string-prefix-p "/iCloud" (mu4e-message-field msg :maildir))))
            :vars '(
                    (user-mail-address . "e.litherlandsmith@icloud.com")
                    (mu4e-sent-folder . "/iCloud/Sent Messages")
                    (mu4e-trash-folder . "/iCloud/Deleted Messages")
                    (mu4e-refile-folder . "/iCloud/Archive")
                    (message-cite-style . message-cite-style-thunderbird)
                    (message-signature . (concat "Evelyn Litherland-Smith (she/they)\n"))
                    ))
           (make-mu4e-context
            :name "Work"
            :match-func (lambda (msg) (when msg (string-prefix-p "/Outlook" (mu4e-message-field msg :maildir))))
            :vars '(
                    (user-mail-address . "evie.litherland-smith@ukaea.uk")
                    (mu4e-sent-folder . "/Outlook/Sent")
                    (mu4e-trash-folder . "/Outlook/Trash")
                    (mu4e-refile-folder . "/Outlook/Archive")
                    (message-cite-style . message-cite-style-outlook)
                    (message-signature . (concat "Evelyn Litherland-Smith (she/they)\n"
                                                 "Spectroscopy Diagnostic Physicist\n"
                                                 "Plasma Science and Fusion Operations\n"
                                                 "UK Atomic Energy Authority"))
                    )))))

  (with-eval-after-load 'mu4e
    (require 'mu4e-modeline)
    (setq mu4e-modeline-all-read '("R:" . "R:")
          mu4e-modeline-all-clear '("C:" . "C:")
          mu4e-modeline-new-items '("N:" . "N:")
          mu4e-modeline-unread-items '("U:" . "U:"))
    (mu4e-modeline-mode +1))

  (with-eval-after-load 'mu4e
    (setq mu4e-search-full-label '("F" . " ")
          mu4e-search-hide-label '("H" . " ")
          mu4e-search-related-label '("R" . " ")
          mu4e-search-skip-duplicates-label '("D" . " ")
          mu4e-search-threaded-label'("T" . " ")
          mu4e-headers-draft-mark '("D" . " ")
          mu4e-headers-flagged-mark '("F" . " ")
          mu4e-headers-unread-mark '("u" . " ")
          mu4e-headers-passed-mark '("P" . " ")
          mu4e-headers-replied-mark '("R" . " ")
          mu4e-headers-trashed-mark '("T" . " ")
          mu4e-headers-attach-mark '("a" . " ")
          mu4e-headers-calendar-mark '("c" . " ")
          mu4e-headers-encrypted-mark '("x" . " ")
          mu4e-headers-signed-mark '("s" . " ")
          mu4e-headers-list-mark '("l" . " ")
          mu4e-headers-personal-mark '("p" . " ")
          mu4e-headers-seen-mark '("S" . " ")
          mu4e-headers-new-mark '("N" . " ")
          mu4e-headers-from-or-to-prefix '("   " . "To ")
          mu4e-headers-thread-root-prefix          '("* " . "* ")
          mu4e-headers-thread-duplicate-prefix     '("= " . "= ")
          mu4e-headers-thread-blank-prefix         '("  " . "  ")
          mu4e-headers-thread-single-orphan-prefix '("─>" . "─>")
          mu4e-headers-thread-orphan-prefix        '("┬>" . "┬>")
          mu4e-headers-thread-connection-prefix    '("│ " . "│ ")
          mu4e-headers-thread-first-child-prefix   '("├>" . "├>")
          mu4e-headers-thread-child-prefix         '("├>" . "├>")
          mu4e-headers-thread-last-child-prefix    '("└>" . "╰>")))

  (with-eval-after-load 'mu4e
    (setq mu4e-marks '((refile :char
                               ("r" . " ")
                               :prompt "refile" :dyn-target
                               (lambda
                                 (target msg)
                                 (mu4e-get-refile-folder msg))
                               :action
                               (lambda
                                 (docid msg target)
                                 (mu4e--server-move docid
                                                    (mu4e--mark-check-target target)
                                                    "-N")))
                       (delete :char
                               ("D" . " ")
                               :prompt "Delete" :show-target
                               (lambda
                                 (target)
                                 "delete")
                               :action
                               (lambda
                                 (docid msg target)
                                 (mu4e--server-remove docid)))
                       (flag :char
                             ("+" . " ")
                             :prompt "+flag" :show-target
                             (lambda
                               (target)
                               "flag")
                             :action
                             (lambda
                               (docid msg target)
                               (mu4e--server-move docid nil "+F-u-N")))
                       (move :char
                             ("m" . " ")
                             :prompt "move" :ask-target mu4e--mark-get-move-target :action
                             (lambda
                               (docid msg target)
                               (mu4e--server-move docid
                                                  (mu4e--mark-check-target target)
                                                  "-N")))
                       (read :char
                             ("!" . " ")
                             :prompt "!read" :show-target
                             (lambda
                               (target)
                               "read")
                             :action
                             (lambda
                               (docid msg target)
                               (mu4e--server-move docid nil "+S-u-N")))
                       (trash :char
                              ("d" . " ")
                              :prompt "dtrash" :dyn-target
                              (lambda
                                (target msg)
                                (mu4e-get-trash-folder msg))
                              :action
                              (lambda
                                (docid msg target)
                                (mu4e--server-move docid
                                                   (mu4e--mark-check-target target)
                                                   "+T-N")))
                       (unflag :char
                               ("-" . " ")
                               :prompt "-unflag" :show-target
                               (lambda
                                 (target)
                                 "unflag")
                               :action
                               (lambda
                                 (docid msg target)
                                 (mu4e--server-move docid nil "-F-N")))
                       (untrash :char
                                ("=" . " ")
                                :prompt "=untrash" :show-target
                                (lambda
                                  (target)
                                  "untrash")
                                :action
                                (lambda
                                  (docid msg target)
                                  (mu4e--server-move docid nil "-T")))
                       (unread :char
                               ("?" . " ")
                               :prompt "?unread" :show-target
                               (lambda
                                 (target)
                                 "unread")
                               :action
                               (lambda
                                 (docid msg target)
                                 (mu4e--server-move docid nil "-S+u-N")))
                       (unmark :char " " :prompt "unmark" :action
                               (mu4e-error "No action for unmarking"))
                       (action :char
                               ("a" . " ")
                               :prompt "action" :ask-target
                               (lambda nil
                                 (mu4e-read-option "Action: " mu4e-headers-actions))
                               :action
                               (lambda
                                 (docid msg actionfunc)
                                 (save-excursion
                                   (when
                                       (mu4e~headers-goto-docid docid)
                                     (mu4e-headers-action actionfunc)))))
                       (something :char
                                  ("*" . " ")
                                  :prompt "*something" :action
                                  (mu4e-error "No action for deferred mark")))))

  (with-eval-after-load 'mu4e
    (require 'mu4e-notification)
    (setq mu4e-notification-support t))

  (use-package gnus-icalendar
    :after (org-agenda)
    :custom
    (gnus-icalendar-org-capture-file (expand-file-name "calendar/email.org.gpg" org-directory))
    (gnus-icalendar-org-capture-headline '("Inbox"))
    :config
    (require 'org-agenda)
    (require 'org-capture)
    (gnus-icalendar-org-setup))

  (use-package mu4e-icalendar
    :after (mu4e org-agenda)
    :custom
    (mu4e-icalendar-trash-after-reply nil)
    :config
    (require 'gnus-icalendar)
    (mu4e-icalendar-setup)
    (gnus-icalendar-org-setup))

IRC

Development Tools

Other

Initial copy from init.el

  (use-package pixel-scroll
    :init
    (pixel-scroll-precision-mode +1))

  (use-package mwheel
    :custom
    (mouse-wheel-scroll-amount '(1 ((shift) . 1)))
    (mouse-wheel-progressive-speed nil)
    (mouse-wheel-follow-mouse t))

  ;; Quick bind for calling `git-sync-all'
  (defun my/git-sync-all ()
    "Run shell command `git-sync-all' asynchronously."
    (interactive)
    (async-shell-command "git-sync-all" "*git-sync-all*" "*git-sync-errors*"))
  (keymap-global-set "C-c g s" #'my/git-sync-all)

  ;; Make shebang (#!) file executable when saved
  (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)

  ;; Remap `upcase-word' and `downcase-word' to DWIM versions
  (keymap-global-set "<remap> <upcase-word>" 'upcase-dwim)
  (keymap-global-set "<remap> <downcase-word>" 'downcase-dwim)

  (setq backup-directory-alist '(("." . "~/.local/state/emacs/backups")))

  (use-package savehist
    :demand
    :config
    (savehist-mode +1))

  (use-package dired
    :custom
    (dired-auto-revert-buffer t)
    (dired-dwim-target t))

  (use-package recentf
    :config
    (run-at-time nil (* 5 60) 'recentf-save-list)
    (recentf-mode +1)
    :custom
    (recentf-max-saved-items 2048))

  (use-package auth-source
    :custom
    (auth-sources '("secrets:Login")))

  (use-package auth-source-pass
    :requires auth-source
    :config
    (auth-source-pass-enable))

  ;; Bind extra `describe-*' commands
  (keymap-global-set "C-h K" #'describe-keymap)

  ;; turn on spell checking, if available.
  (use-package ispell
    :custom
    (ispell-dictionary "en_GB"))

  (use-package flyspell
    :hook ((text-mode . flyspell-mode)
           (prog-mode . flyspell-prog-mode))
    :init
    (require 'ispell)
    :custom
    (flyspell-use-meta-tab nil))

  (use-package flyspell-correct
    :if (package-installed-p 'flyspell-correct)
    :after flyspell
    :bind ( :map flyspell-mode-map
            ("C-;" . flyspell-correct-wrapper)))

  (use-package ibuffer
    :defines ibuffer-filter-groups
    :bind (("C-c b" . ibuffer)))

  (use-package shell
    :bind (("C-c t s" . shell)))

  (use-package eshell
    :bind (("C-c t e" . eshell))
    :config
    (require 'esh-mode))

  (use-package esh-mode
    :defines eshell-mode-map
    :bind ( :map eshell-mode-map
            ("<remap> <eshell-previous-matching-input>" . consult-history)))

  (use-package calc
    :bind (("<Calculator>" . calc)))

  (use-package xref
    :custom
    (xref-show-definitions-function 'xref-show-definitions-completing-read))

  (use-package appt
    :custom
    (appt-display-diary nil)
    (appt-display-format 'echo))

  (use-package calendar
    :after appt
    :bind (("C-c >" . calendar))
    :hook ((calendar-today-visible . calendar-mark-today))
    :custom
    (calendar-date-style 'iso)
    (calendar-mark-holidays-flag t)
    (calendar-mark-diary-entries-flag nil)
    (calendar-view-holidays-initially-flag nil)
    (calendar-view-diary-initially-flag nil)
    :config
    (appt-activate +1)
    (add-to-list 'display-buffer-alist
                 '("\\*Calendar\\*"
                   (display-buffer-in-side-window)
                   (side . bottom)
                   (slot . 0)
                   (window-height . 0.2)
                   (window-parameters . ((no-delete-other-windows . t))))))

  (add-hook 'prog-mode-hook #'(lambda () (display-line-numbers-mode +1)))

  (use-package which-key
    :if (package-installed-p 'which-key)
    :functions which-key-mode
    :config (which-key-mode +1))

  (use-package elec-pair
    :config
    (electric-pair-mode +1))

  (use-package paren
    :config
    (show-paren-mode +1))

  ;; add visual pulse when changing focus, like beacon but built-in
  ;; from from https://karthinks.com/software/batteries-included-with-emacs/
  (defun pulse-line (&rest _)
    "Pulse the current line."
    (pulse-momentary-highlight-one-line (point)))

  (use-package base16-theme
    :if (package-installed-p 'base16-theme)
    :demand
    :defines (base16-one-light-theme-colors
              my/load-theme-and-configure)
    :hook (server-after-make-frame . my/load-theme-and-configure)
    :custom
    (base16-theme-distinct-fringe-background nil)
    (base16-theme-highlight-mode-line 'contrast)
    :init
    (defun my/load-theme-and-configure ()
      "Load theme and configure some faces."
      (load-theme 'base16-one-light t)

      ;; Change outline headers to follow rainbow order
      (require 'outline)
      (dolist (pairing '((outline-1 . :base08)
                         (outline-2 . :base09)
                         (outline-3 . :base0A)
                         (outline-4 . :base0B)
                         (outline-5 . :base0C)
                         (outline-6 . :base0D)
                         (outline-7 . :base0E)
                         (outline-8 . :base0F)))
        (set-face-attribute (car pairing) nil
                            :foreground
                            (plist-get base16-one-light-theme-colors (cdr pairing)))))
    :config
    (if (display-graphic-p) (my/load-theme-and-configure)))

  (use-package nerd-icons
    :if (package-installed-p 'nerd-icons)
    :functions (nerd-icons-octicon))

  (use-package nerd-icons-dired
    :requires nerd-icons
    :hook (dired-mode))

  (use-package nerd-icons-ibuffer
    :requires nerd-icons
    :hook (ibuffer-mode))

  (use-package nerd-icons-completion
    :functions nerd-icons-completion-mode
    :requires nerd-icons
    :hook (after-init . (lambda () (nerd-icons-completion-mode +1))))

  (use-package ligature
    :if (package-installed-p 'ligature)
    :functions (ligature-set-ligatures
                global-ligature-mode)
    :config
    (ligature-set-ligatures
     '(text-mode prog-mode org-mode)
     '("->" "-->" "--->" "=>" "==>" "===>"
       "<--" "<---" "<-" "<==" "<==="
       "<->" "<-->" "<--->" "<---->"
       "<=>" "<==>" "<===>" "<====>"
       "<=" ">=" "==" "!=" "===" "!==" "!==="
       "/*" "*/" "+++" "<~~" "~~>" "<!---" "---!>"))
    (global-ligature-mode +1))

  (use-package whitespace
    :custom
    (whitespace-style '(face
                        empty
                        trailing
                        tab-mark
                        indentation::space))
    (whitespace-action '(report-on-bogus
                         cleanup
                         auto-cleanup)))

  (setq mode-line-compact 'long)

  (line-number-mode -1)
  (column-number-mode -1)
  (size-indication-mode +1)

  ;; (require 'battery)
  ;; (when (and battery-status-function
  ;;            (not ( string-match-p "unknown"
  ;;                   ( battery-format "%B"
  ;;                     (funcall battery-status-function)))))
  ;;   (display-battery-mode +1))

  (use-package time
    :custom
    (display-time-24hr-format t))

  (use-package diff-hl
    :if (package-installed-p 'diff-hl)
    :functions (diff-hl-magit-pre-refresh
                diff-hl-magit-post-refresh
                global-diff-hl-mode)
    :init
    (add-hook 'magit-pre-refresh-hook #'diff-hl-magit-pre-refresh)
    (add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh)
    :custom
    (diff-hl-disable-on-remote nil)
    (diff-hl-draw-borders t)
    :config
    (global-diff-hl-mode))

  (setq split-height-threshold nil
        split-width-threshold 160)

  (use-package winner
    :demand
    :config
    (winner-mode))

  (use-package ediff
    :bind (("C-c d f" . ediff-files)
           ("C-c d b" . ediff-buffers)
           ("C-c d 3 f" . ediff-files3)
           ("C-c d 3 b" . ediff-buffers3))
    :custom
    (ediff-window-setup-function #'ediff-setup-windows-plain))

  (use-package emms
    :defines (emms-browser-mode-map
              emms-playlist-mode-map)
    :functions (emms-all
                emms-default-players
                emms-mpris-enable
                emms-cache-enable
                emms-show)
    :bind (("C-c e e" . emms-smart-browse)
           ("C-c e p" . emms-pause)
           ("C-c e s" . emms-stop)
           ("C-c e z" . emms-toggle-repeat-track)
           ("C-c e C-r" . emms-toggle-repeat-playlist)
           ("C-c e C-b" . emms-browser)
           ("C-c e C-p" . emms-playlist-mode-go)
           ("<XF86AudioPlay>" . emms-pause)
           ("<XF86AudioPrev>" . emms-previous)
           ("<XF86AudioNext>" . emms-next)
           :map emms-browser-mode-map
           ("e" . emms-smart-browse)
           ("P" . emms-pause)
           ("S" . emms-stop)
           ("z" . emms-toggle-repeat-track)
           :map emms-playlist-mode-map
           ("e" . emms-smart-browse))
    :custom
    (emms-source-file-default-directory "~/Music/")
    (emms-lyrics-dir (expand-file-name "lyrics" "~/Music/"))
    (emms-browser-covers #'emms-browser-cache-thumbnail-async)
    (emms-browser-default-covers (list (expand-file-name "placeholder.jpg" "~/Music/")))
    (emms-repeat-playlist t)
    :config
    (emms-all)
    (emms-default-players)
    (emms-mpris-enable)
    (emms-cache-enable)
    (add-hook 'emms-player-started-hook #'emms-show))

  (use-package org
    :demand
    :defines org-mode-map
    :hook ((org-mode . turn-on-auto-fill))
    :bind ( :map org-mode-map
            ("<remap> <imenu>" . consult-org-heading)
            ("<remap> <org-goto>" . consult-org-heading))
    :custom
    (org-directory "~/Documents/org")
    (org-default-notes-file (expand-file-name "notes.org" org-directory))
    (org-archive-location "::* Archived Tasks")
    (org-hide-emphasis-markers t)
    (org-use-sub-superscripts '{})
    (org-pretty-entities t)
    (org-pretty-entities-include-sub-superscripts t)
    (org-fontify-done-headline t)
    (org-fontify-todo-headline t)
    (org-fontify-emphasized-text t)
    (org-fontify-quote-and-verse-blocks t)
    (org-tags-column 0)
    (org-enforce-todo-dependencies t)
    (org-enforce-todo-checkbox-dependencies t)
    (org-yank-folded-subtrees nil)
    (org-yank-adjusted-subtrees t)
    (org-display-remote-inline-images 'cache)
    (org-M-RET-may-split-line '((default . nil)
                                (headline . nil)
                                (item . nil)
                                (table . t))))

  (use-package ox
    :after org
    :custom
    (org-export-with-sub-superscripts org-use-sub-superscripts))

  (use-package org-faces
    :after org
    :config
    ;; Ensure tables and src blocks use fixed-pitch font.
    (set-face-attribute 'org-block nil :inherit 'fixed-pitch)
    (set-face-attribute 'org-code nil :inherit 'fixed-pitch)
    (set-face-attribute 'org-table nil :inherit 'fixed-pitch)
    (set-face-attribute 'org-verbatim nil :inherit 'org-code)
    ;; Let quote and verse blocks use variable-pitch font, if configured
    (set-face-attribute 'org-quote nil :inherit 'variable-pitch)
    (set-face-attribute 'org-verse nil :inherit 'variable-pitch))

  (use-package org-keys
    :after org
    :custom
    (org-return-follows-link t)
    (org-mouse-1-follows-link t))

  (use-package org-indent
    :after org
    :hook org-mode)

  (use-package org-attach
    :after org
    :custom
    (org-attach-id-dir (expand-file-name "data/" org-directory))
    (org-attach-dir-relative t)
    (org-attach-use-inheritance nil))

  (use-package org-refile
    :after (org org-agenda)
    :bind ("M-g r" . org-refile-goto-last-stored)
    :custom
    (org-outline-path-complete-in-steps nil)
    (org-refile-use-outline-path t)
    (org-refile-allow-creating-parent-nodes t)
    (org-refile-use-outline-path 'file)
    (org-refile-targets '((nil . (:maxlevel . 3))
                          (org-agenda-files . (:maxlevel . 3)))))

  (use-package org-src
    :after org
    :custom
    (org-src-window-setup 'reorganize-frame))

  (use-package ob-core
    :after org
    :custom
    (org-babel-no-eval-on-ctrl-c-ctrl-c t)
    (org-confirm-babel-evaluate nil)
    :config
    (let (babel-languages)
      (push '(lua . t) babel-languages)
      (push '(python . t) babel-languages)
      (push '(emacs-lisp . t) babel-languages)
      (org-babel-do-load-languages 'org-babel-load-languages babel-languages)))

  (use-package ob-python
    :after ob-core
    :custom
    (org-babel-python-command "python3"))

  (use-package org-capture
    :after org
    :bind (("C-c o n" . org-capture)
           ("M-g o" . org-capture-goto-last-stored))
    :custom (org-capture-templates
             '(("t" "TODO" entry
                (file+olp "tasks.org.gpg" "Inbox")
                "* TODO %?\nDEADLINE: %t\n %i\n %a")
               ("#" "used by gnus-icalendar-org" entry
                (file+olp "calendar/email.org.gpg" "Inbox")
                "%i" :immediate-finish t))))

  (if (executable-find "sqlite3")
      (use-package org-roam
        :if (package-installed-p 'org-roam)
        :after org
        :defines org-roam-directory
        :functions org-roam-db-autosync-mode
        :bind (("C-c o r i" . org-roam-node-insert)
               ("C-c o r f" . org-roam-node-find)
               ("C-c o r n" . org-roam-capture)
               ("C-c o r j" . org-roam-dailies-capture-today)
               ("M-g j" . org-roam-dailies-goto-today)
               ("M-g C-j" . org-roam-dailies-goto-date)
               :map org-mode-map
               ("C-c o r b" . org-roam-buffer-toggle))
        :custom
        (org-roam-directory (expand-file-name "roam" org-directory))
        (org-roam-completion-everywhere nil)
        (org-roam-node-display-template (concat
                                         "${title:*} "
                                         (propertize "${tags:24}" 'face 'org-tag)))
        (org-roam-capture-templates '(("d" "default" plain "%?"
                                       :target (file+head "${slug}.org" "#+title: ${title}\n")
                                       :unnarrowed t)))
        :config
        (mkdir org-roam-directory t)
        (add-to-list 'display-buffer-alist
                     '("\\*org-roam\\*"
                       (display-buffer-in-side-window)
                       (side . right)
                       (slot . 0)
                       (window-width . 0.33)
                       (window-parameters . ((no-delete-other-windows . t)))))
        (org-roam-db-autosync-mode +1)))

  (use-package org-roam-dailies
    :after org-roam
    :custom
    (org-roam-dailies-directory "./")
    (org-roam-dailies-capture-templates
     '(("d" "default" entry
        "* %?"
        :target (file+datetree "journal.org.gpg" week)))))

  (use-package org-clock
    :after org
    :custom
    (org-clock-rounding-minutes 15))

  (use-package org-agenda
    :after (org appt)
    :bind (("C-c o a" . org-agenda)
           ("C-c o C-a" . org-agenda-list))
    :hook (org-agenda-finalize . org-agenda-to-appt)
    :custom
    (org-agenda-span 'day)
    (org-agenda-start-on-weekday 1)
    (org-agenda-sticky nil)
    (org-agenda-window-setup 'current-window)
    (org-agenda-tags-column 0)
    (org-agenda-diary-file (expand-file-name "calendar/diary.org.gpg" org-directory))
    (org-agenda-include-diary nil)
    (org-agenda-include-deadlines t)
    (org-agenda-todo-ignore-scheduled 'future)
    (org-agenda-todo-ignore-deadlines 'far)
    (org-agenda-clockreport-parameter-plist '(:link t :maxlevel 6 :emphasize t :stepskip0 t :fileskip0 t :filetitle t :properties ("WON")))
    (org-agenda-start-with-clockreport-mode t)
    (org-agenda-start-with-log-mode nil)
    (org-agenda-prefix-format '((agenda . " %-12:c%?-12t% s")
                                (todo . " %-12:c")
                                (tags . " %-12:c")
                                (search . " %-12:c")))
    (org-agenda-file-regexp "\\`[^.].*\\.org\\\(\\.gpg\\\)?\\'")
    (org-agenda-files (list
                       (expand-file-name org-directory)
                       (expand-file-name "calendar" org-directory)
                       (expand-file-name "roam/journal.org.gpg" org-directory)
                       (expand-file-name "roam/analysis_notes.org" org-directory)))
    :config
    (appt-activate +1))

  (use-package ox-icalendar
    :after org
    :custom
    (org-icalendar-store-UID t)
    (org-icalendar-alarm-time 15)
    (org-icalendar-include-body t)
    (org-icalendar-include-sexps t)
    (org-icalendar-include-todo t)
    (org-icalendar-combined-name "org-mode")
    (org-icalendar-combined-description "Emacs org-mode combined export"))

  (use-package org-noter
    :after (org doc-view citar)
    :commands org-noter
    :custom
    (org-noter-always-create-frame nil)
    (org-noter-kill-frame-at-session-end nil)
    (org-noter-auto-save-last-location t)
    (org-noter-default-notes-file-names '("noter.org"))
    (org-noter-doc-property-in-notes t)
    (org-noter-notes-search-path
     (list (expand-file-name "notes" org-directory)
           (car citar-notes-paths)))
    (org-noter-prefer-root-as-file-level nil))

  (use-package citar
    :defines (citar-bibliography
              citar-indicators)
    :functions (citar-indicator-create
                citar-has-files
                citar-has-links
                citar-has-notes
                citar-is-cited)
    :hook
    (LaTeX-mode . citar-capf-setup)
    (org-mode . citar-capf-setup)
    :custom
    (citar-bibliography
     (list
      (expand-file-name "citar/main.bib" org-directory)))
    (citar-notes-paths
     (list
      (expand-file-name "citar/notes/" org-directory)))
    (citar-library-paths
     (list
      (expand-file-name "~/Documents/library/")))
    :config
    (require 'org)
    (require 'nerd-icons)
    (setopt org-cite-insert-processor 'citar
            org-cite-follow-processor 'citar
            org-cite-activate-processor 'citar)
    (dolist (bibfile citar-bibliography)
      (add-to-list 'org-cite-global-bibliography bibfile))
    (defvar citar-indicator-files-icons
      (citar-indicator-create
       :symbol (nerd-icons-octicon
                "nf-oct-file"
                :face 'nerd-icons-green
                :v-adjust -0.1)
       :function #'citar-has-files
       :padding "  " ; need this because the default padding is too low for these icons
       :tag "has:files"))
    (defvar citar-indicator-links-icons
      (citar-indicator-create
       :symbol (nerd-icons-octicon
                "nf-oct-link"
                :face 'nerd-icons-orange
                :v-adjust 0.01)
       :function #'citar-has-links
       :padding "  " ; need this because the default padding is too low for these icons
       :tag "has:links"))
    (defvar citar-indicator-notes-icons
      (citar-indicator-create
       :symbol (nerd-icons-octicon
                "nf-oct-note"
                :face 'nerd-icons-blue
                :v-adjust -0.3)
       :function #'citar-has-notes
       :padding "  " ; need this because the default padding is too low for these icons
       :tag "has:notes"))
    (defvar citar-indicator-cited-icons
      (citar-indicator-create
       :symbol (nerd-icons-octicon
                "nf-oct-circle"
                :face 'nerd-icon-green)
       :function #'citar-is-cited
       :padding "  " ; need this because the default padding is too low for these icons
       :tag "is:cited"))
    (setq citar-indicators (list citar-indicator-files-icons
                                 citar-indicator-links-icons
                                 citar-indicator-notes-icons
                                 citar-indicator-cited-icons)))

  (setq org-latex-compiler "lualatex")
  (setq org-preview-latex-default-process 'dvisvgm)

  (use-package vertico
    :if (package-installed-p 'vertico)
    :functions vertico-mode
    :hook (after-init . (lambda () (vertico-mode +1)))
    :custom
    (vertico-cycle t)
    :config
    (require 'vertico-directory))

  (use-package marginalia
    :if (package-installed-p 'marginalia)
    :functions marginalia-mode
    :hook (after-init . (lambda () (marginalia-mode +1)))
    :custom
    (marginalia-annotators '(marginalia-annotators-heavy
                             marginalia-annotators-light
                             nil))
    :config (marginalia-mode +1))

  (use-package orderless
    :if (package-installed-p 'orderless)
    :custom
    (completion-styles '(orderless basic))
    (completion-category-defaults nil)
    (completion-category-overrides '((file (styles basic partial-completion))
                                     (eglot (styles orderless))
                                     (eglot-capf (styles  orderless)))))

  (use-package nerd-icons-corfu
    :functions nerd-icons-corfu-formatter
    :requires nerd-icons)

  (use-package corfu
    :if (package-installed-p 'corfu)
    :defines (corfu-map
              corfu-mode-map
              corfu-margin-formatters)
    :functions (corfu-mode
                global-corfu-mode
                corfu-history-mode)
    :hook ((after-init . (lambda () (global-corfu-mode +1)))
           (minibuffer-setup . (lambda ()
                                 "Enable `corfu-mode' for `M-:' and `M-!'."
                                 (when (local-variable-p 'completion-at-point-functions)
                                   (corfu-mode +1)))))
    :bind ( :map corfu-map
            ("M-SPC" . corfu-insert-separator)
            ("RET" . nil)
            ("TAB" . corfu-insert)
            ([tab] . corfu-insert))
    :custom
    (corfu-cycle t)
    (corfu-auto nil)
    (corfu-preselect 'directory)
    :config
    (require 'corfu-history)
    (when (require 'nerd-icons-corfu nil :noerror)
      (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
    (when (require 'corfu-popupinfo nil :noerror)
      (corfu-popupinfo-mode +1))
    (when (and (require 'corfu-terminal nil :noerror)
               (not (display-graphic-p)))
      (corfu-terminal-mode +1)))

  (use-package corfu-history
    :requires (corfu savehist)
    :functions corfu-history
    :config
    (add-to-list 'savehist-additional-variables #'corfu-history))

  (use-package corfu-popupinfo
    :requires corfu
    :defines corfu-popupinfo-map
    :functions corfu-popupinfo-mode
    :bind ( :map corfu-popupinfo-map
            ("M-d" . corfu-popupinfo-toggle)
            ("M-n" . corfu-popupinfo-scroll-up)
            ("M-p" . corfu-popupinfo-scroll-down))
    :custom
    (corfu-popupinfo-delay 0.3))

  (use-package corfu-terminal
    :requires corfu
    :functions corfu-terminal-mode)

  (use-package cape
    :if (package-installed-p 'cape)
    :after corfu
    :functions (cape-emoji
                cape-file
                cape-dabbrev)
    :hook ((conf-mode prog-mode text-mode) . (lambda ()
                                               (dolist (cape-fn '(cape-dabbrev
                                                                  cape-file
                                                                  cape-emoji))
                                                 (add-hook 'completion-at-point-functions cape-fn nil t))))
    :custom
    (cape-dabbrev-min-length (+ corfu-auto-prefix 1)))

  (use-package consult
    :if (package-installed-p 'consult)
    :functions (consult-org-heading
                consult-history)
    :bind (("<remap> <imenu>" . consult-imenu)
           ("<remap> <switch-to-buffer>" . consult-buffer)
           ("<remap> <project-switch-to-buffer>" . consult-project-buffer)
           ("C-c s l" . consult-line)
           ("C-c s o" . consult-outline)
           ("C-c s f" . consult-fd)
           ("C-c s g" . consult-ripgrep)
           ("C-c s e" . consult-flymake)
           ("C-c s i" . consult-info)
           :map minibuffer-local-map
           ("<remap> <previous-matching-history-element>" . consult-history)
           :map comint-mode-map
           ("<remap> <comint-history-isearch-backward-regexp>" . consult-history)))

  (use-package consult-eglot
    :after (consult eglot)
    :bind (("C-c s s" . consult-eglot-symbols)))

  (use-package tempel
    :if (package-installed-p 'tempel)
    :defines tempel-path
    :functions (tempel-expand
                tempel-abbrev-mode)
    :bind (("M-+" . tempel-complete)
           ("M-*" . tempel-insert))
    :hook ((conf-mode prog-mode text-mode) . (lambda () (add-hook 'completion-at-point-functions 'tempel-complete nil t)))
    :custom
    (tempel-trigger-prefix "+"))

  (require 'tramp)
  (setq org-publish-project-alist
        `(("xenia.me.uk"
           :base-directory ,(expand-file-name "www/xenia.me.uk" "~/Projects/")
           :base-extension "org"
           :exclude "setup.org"
           :recursive t
           :publishing-function org-html-publish-to-html
           :publishing-directory "/sshx:pixelifytica@legion:/var/www/xenia.me.uk"
           :auto-sitemap t
           :sitemap-title "Sitemap"
           :section-numbers nil
           :with-author t
           :with-email t
           :with-toc nil
           :html-link-home "/"
           :html-link-up "../"
           ;; :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"css/style.css\"/>"
           )))

  (use-package rainbow-delimiters
    :if (package-installed-p 'rainbow-delimiters)
    :hook (prog-mode))

  (use-package envrc
    :if (package-installed-p 'envrc)
    :hook (after-init . envrc-global-mode)
    :custom
    (envrc-show-summary-in-minibuffer t))

  (use-package gud
    :defer t
    :defines gdb-many-windows
    :config
    (setq gdb-many-windows t))

  (use-package treesit
    :custom
    (treesit-font-lock-level 3))

  (use-package treesit-auto
    :if (package-installed-p 'treesit-auto)
    :requires treesit
    :functions (treesit-auto-add-to-auto-mode-alist
                global-treesit-auto-mode)
    :hook (after-init . (lambda () (global-treesit-auto-mode +1)))
    :config
    (treesit-auto-add-to-auto-mode-alist))

  (use-package eldoc
    :custom
    (eldoc-echo-area-display-truncation-message nil)
    (eldoc-echo-area-prefer-doc-buffer 'maybe)
    (eldoc-echo-area-use-multiline-p 3))

  (use-package eglot
    :bind ( :map prog-mode-map
            ("C-c c a" . eglot-code-actions)
            ("C-c c r" . eglot-rename))
    :hook ((eglot-managed-mode . (lambda () (add-hook 'flymake-diagnostic-functions 'eglot-flymake-backend nil t)))
           (nix-mode . (lambda () (if (executable-find "nixd" t) (eglot-ensure))))
           (python-base-mode . (lambda () (if (executable-find "pylsp" t) (eglot-ensure))))
           (lua-mode . (lambda () (if (executable-find "lua-language-server" t) (eglot-ensure))))
           ((rust-ts-mode rust-mode) . (lambda () (if (executable-find "rust-analyzer" t) (eglot-ensure))))
           ((js-base-mode typescript-ts-base-mode) . (lambda () (if (executable-find "typescript-language-server" t) (eglot-ensure))))
           )
    :custom
    (eglot-menu-string "lsp")
    (eglot-send-changes-idle-time 1)
    (eglot-extend-to-xref t)
    (eglot-autoshutdown t)
    (eglot-sync-connect nil)
    (eglot-autoreconnect (* 60 5))
    (eglot-events-buffer-config '(:size 0))
    (eglot-ignored-server-capabilities
     '(:documentHighlightProvider
       :documentFormattingProvider
       :documentRangeFormattingProvider
       :documentOnTypeFormattingProvider
       :documentLinkProvider
       :colorProvider
       :foldingRangeProvider))
    :init
    (setq eglot-stay-out-of '(flymake))
    :config
    (setq-default eglot-workspace-configuration
                  '( :nil ( :nix
                            ( :maxMemoryMB nil
                              :flake
                              ( :autoArchive t
                                :nixpkgsInputName "nixpkgs")))
                     :pylsp ( :plugins
                              ( :autopep8 (:enabled nil)
                                :flake8 (:enabled nil)
                                :jedi_completion ( :enabled t
                                                   :include_params t
                                                   :include_class_objects t
                                                   :include_function_objects t
                                                   :fuzzy t)
                                :jedi_definition (:enabled t)
                                :jedi_hover (:enabled t)
                                :mccabe (:enabled nil)
                                :preload (:enabled nil)
                                :pycodestyle (:enabled nil)
                                :pydocstyle (:enabled nil)
                                :pyflakes (:enabled nil)
                                :pylint (:enabled nil)
                                :rope_autoimport ( :completions (:enabled t)
                                                   :code_actions (:enabled t))
                                :rope_completion (:enabled t)
                                :yapf (:enabled nil)))))
    )

  (use-package apheleia
    :if (package-installed-p 'apheleia)
    :defines (apheleia-formatters
              apheleia-mode-alist)
    :bind (("C-c c f" . apheleia-format-buffer))
    :hook (prog-mode)
    :custom (apheleia-remote-algorithm 'cancel)
    :config
    (add-to-list 'apheleia-mode-alist '(python-ts-mode . (ruff ruff-isort)))
    (add-to-list 'apheleia-mode-alist '(python-mode . (ruff ruff-isort))))

  (use-package flymake
    :bind (("C-c C-." . flymake-goto-next-error)
           ("C-c C-," . flymake-goto-prev-error))
    :hook ((prog-mode yaml-ts-mode) . (lambda () (flymake-mode +1)))
    :custom
    (flymake-no-changes-timeout 1)
    (flymake-show-diagnostics-at-end-of-line 'short))

  (use-package flymake-shellcheck
    :if (package-installed-p 'flymake-shellcheck)
    :functions flymake-shellcheck-load
    :requires flymake
    :hook (sh-mode . (lambda () (if (executable-find "shellcheck" t)
                                    (flymake-shellcheck-load)))))

  (use-package flymake-yamllint
    :if (package-installed-p 'flymake-yamllint)
    :functions flymake-yamllint-setup
    :requires flymake
    :hook (yaml-ts-mode . (lambda () (if (executable-find "yamllint" t)
                                         (flymake-yamllint-setup)))))

  (use-package flymake-clippy
    :if (package-installed-p 'flymake-clippy)
    :functions flymake-clippy-setup-backend
    :requires flymake
    :hook (rust-mode . (lambda () (if (executable-find "clippy" t)
                                      (flymake-clippy-setup-backend)))))

  (use-package flymake-eslint
    :if (package-installed-p 'flymake-eslint)
    :functions flymake-eslint-enable
    :requires flymake
    :hook ((js-base-mode typescript-ts-base-mode) . (lambda () (if (executable-find "eslint" t)
                                                                   (flymake-eslint-enable)))))

  (use-package project
    :functions (project-forget-zombie-projects
                project-remember-projects-under)
    :custom
    (project-switch-use-entire-map t)
    (project-switch-commands '((project-find-file "Find file")
                               (project-find-regexp "Find regexp")
                               (project-find-dir "Find directory")
                               (project-eshell "Eshell")
                               (magit-project-status "Magit")))
    :config
    (defun my/project-find-common-projects ()
      "Search and remember common project directories.

  Calls `project-remember-projects-under' for ~/Projects/"
      (interactive)
      (require 'project)
      (project-forget-zombie-projects)
      (project-remember-projects-under user-emacs-directory nil)
      (project-remember-projects-under "/etc/nixos/" nil)
      (project-remember-projects-under "~/Projects" t)
      ))

  (use-package magit
    :bind (("C-c g g" . magit-status)
           ("C-c g d" . magit-dispatch)
           ("C-c g f" . magit-file-dispatch)
           ("C-c g b" . magit-blame-addition)
           ("<remap> <project-vc-dir>" . magit-project-status)
           :map project-prefix-map
           ("m" . magit-project-status))
    :custom
    (magit-display-buffer-function 'magit-display-buffer-same-window-except-diff-v1)
    (magit-define-global-key-bindings nil)
    (magit-show-long-lines-warning nil)
    (magit-clone-default-directory "~/Projects/")
    (magit-clone-set-remote.pushDefault t)
    (magit-commit-show-diff t)
    (magit-commit-diff-inhibit-same-window t)
    (magit-diff-adjust-tab-width t)
    (magit-diff-refine-hunk 'all)
    (magit-diff-refine-ignore-whitespace t)
    (magit-clone-name-alist '(("\\`\\(?:github:\\|gh:\\)?\\([^:]+\\)\\'" "github.com" "github.user")
                              ("\\`\\(?:gitlab:\\|gl:\\)\\([^:]+\\)\\'"  "gitlab.com" "gitlab.user")
                              ("\\`\\(?:sourcehut:\\|sh:\\)\\([^:]+\\)\\'" "git.sr.ht" "sourcehut.user")
                              ("\\`\\(?:gitea:\\|gt:\\)\\([^:]+\\)\\'" "git.xenia.me.uk" "gitea.user"))))

  (use-package nix-mode
    :if (package-installed-p 'nix-mode)
    :mode "\\.nix\\'"
    :functions nix-prettify-global-mode
    :config
    (require 'nix)
    (require 'nix-flake)
    (require 'nix-repl)
    (require 'nix-store)
    (nix-prettify-global-mode +1))

  (defun my/enable-fill-column (col)
    "Set and enable fill column to `COL'."
    (set-fill-column col)
    (display-fill-column-indicator-mode +1))

  (use-package python
    :hook ((python-base-mode . (lambda () (my/enable-fill-column 88)))
           (python-base-mode . (lambda ()
                                 (setq-local
                                  python-check-command
                                  (cond
                                   ((executable-find "mypy" t) "mypy --check-untyped-defs --warn-unreachable --show-error-codes --ignore-missing-imports")
                                   (t "pyflakes"))
                                  python-flymake-command
                                  (cond
                                   ((executable-find "ruff" t) '("ruff" "check" "--output-format=concise" "--stdin-filename=stdin" "-"))
                                   ((executable-find "flake8" t) '("flake8" "--max-line-length" "88" "-"))
                                   (t '("pyflakes")))))))
    :custom
    (python-shell-interpreter "python3")
    (python-shell-dedicated nil)
    (python-shell-completion-native-enable nil)
    (python-indent-def-block-scale 1)
    :config
    (setq python-ts-mode-hook python-mode-hook))

  (use-package python-docstring
    :if (package-installed-p 'python-docstring)
    :hook python-base-mode)

  (use-package files
    :custom
    (view-read-only t)
    (enable-remote-dir-locals t))

  (use-package tramp
    :defer t
    :custom
    (tramp-default-method "scpx")
    (tramp-backup-directory-alist backup-directory-alist)
    (tramp-auto-save-directory (cdr (assoc "." tramp-backup-directory-alist)))
    :config
    (add-to-list 'tramp-remote-path 'tramp-own-remote-path)
    (add-to-list 'tramp-remote-path "~/.local/bin/"))

  (connection-local-set-profile-variables
   'remote-no-corfu-auto
   '((corfu-auto . nil)))

  (connection-local-set-profiles
   '(:application tramp)
   'remote-no-corfu-auto)

  (use-package doc-view
    :defer t
    :bind ( :map doc-view-mode-map
            ("<mouse-8>" . doc-view-previous-page)
            ("<mouse-9>" . doc-view-next-page))
    :custom
    (doc-view-resolution 200)
    (doc-view-imenu-enabled t)
    (doc-view-scale-internally t)
    (doc-view-image-width 850))

  (use-package markdown-mode
    :if (package-installed-p 'markdown-mode)
    :hook
    ((markdown-mode . turn-on-auto-fill))
    :custom
    (markdown-enable-math t)
    (markdown-enable-html t)
    :config
    (set-face-attribute 'markdown-code-face nil :inherit 'fixed-pitch)
    (set-face-attribute 'markdown-inline-code-face nil :inherit 'fixed-pitch)
    (set-face-attribute 'markdown-table-face nil :inherit 'fixed-pitch)
    (set-face-attribute 'markdown-blockquote-face nil :inherit 'variable-pitch)
    (set-face-attribute 'markdown-comment-face nil :inherit 'variable-pitch))

  (use-package pandoc-mode
    :if (package-installed-p 'pandoc-mode)
    :after (markdown-mode)
    :hook (markdown-mode . conditionally-turn-on-pandoc))

  (use-package bbdb
    :bind (("M-g b" . bbdb-display-all-records))
    :custom
    (bbdb-file (locate-user-emacs-file "bbdb.gpg")))

  (use-package erc
    :commands erc-compute-nick
    :custom
    (erc-nick (user-login-name))
    (erc-user-full-name (user-full-name)))

  (defun my/libera-chat-connect ()
    "Connect to irc.libera.chat directly."
    (interactive)
    (require 'erc)
    (require 'password-store)
    (erc-tls
     :server "irc.libera.chat"
     :password (password-store-get 'social/irc.libera.chat)))
  (defun my/znc-connect ()
    "Connect to my ZNC IRC bouncer."
    (interactive)
    (require 'erc)
    (require 'password-store)
    (erc-tls
     :server "xenia.me.uk"
     :port 6697
     :nick (concat (erc-compute-nick) "/liberachat")
     :password (password-store-get 'local/znc)))

  (use-package eww
    :defer t
    :custom
    (browse-url-browser-function 'browse-url-default-browser)
    (browse-url-secondary-browser-function 'browse-url-default-browser)
    (browse-url-new-window-flag t)
    (eww-default-download-directory "~/Downloads/")
    (eww-auto-rename-buffer 'title)
    (eww-browse-url-new-window-is-tab nil))

  (use-package password-store
    :defer t
    :functions password-store-get)

  ;; Scratch buffer shortcut
  (keymap-global-set "C-c w x" #'scratch-buffer)

  ;; Config file shortcut
  (defun my/open-config-file ()
    "Open Emacs config file."
    (interactive)
    (find-file (locate-user-emacs-file "README.org")))
  (keymap-global-set "C-c w e" #'my/open-config-file)

  ;; Tempel template file shortcut
  (defun my/open-template-file ()
    "Open `tempel' template file."
    (interactive)
    (require 'tempel)
    (find-file tempel-path))
  (keymap-global-set "C-c w t" #'my/open-template-file)

  ;; Org directory shortcut
  (defun my/open-org-directory ()
    "Open base `org-mode' directory in Dired."
    (interactive)
    (require 'org)
    (find-file org-directory))
  (keymap-global-set "C-c w o" #'my/open-org-directory)

  (defun my/open-global-bibliography ()
    "Open `org-cite-global-bibliography'."
    (interactive)
    (require 'citar)
    (find-file (car org-cite-global-bibliography)))
  (keymap-global-set "C-c w b" #'my/open-global-bibliography)

  (defun my/open-documents-directory ()
    "Open Documents directory."
    (interactive)
    (find-file "~/Documents/"))
  (defun my/open-downloads-directory ()
    "Open Downloads directory."
    (interactive)
    (find-file "~/Downloads/"))
  (keymap-global-set "C-c w d" #'my/open-documents-directory)
  (keymap-global-set "C-c w C-d" #'my/open-downloads-directory)

Font Showcase

This is a showcase of various font features to act as a standard candle.

Font emphasis

Examples of:

  • Bold text
  • Italic text
  • Underscored text
  • Literal text
  • Code
  • Strike-through

Character showcase

ABC.DEF.GHI.JKL.MNO.PQRS.TUV.WXYZ abc.def.ghi.jkl.mno.pqrs.tuv.wxyz
!iIlL17|¦ ¢coO08BbDQ $5SZ2zs 96µm float il1[]={1-2/3.4,5+6=7/8%90};
1234567890 ,._-+= >< «¯-¬_» ~–÷+× {*}[]()<>`+-=$/#_%^@\&|~?'" !,.;:
E3CGQ g9q¶ uvw ſßðþ ΓΔΛαδιλμξπτχ∂ ЖЗКУЯжзклмнруфчьыя <= != == => ->

Legibility test

Can I tell the difference between: 1,i,I,l,L,| How about: 0,O,o

Tables

Heading 1 Heading 2 Plot
1 1
2 4 c
3 9 W
4 16 WV
5 25 WWH
6 36 WWWW:
7 49 WWWWWV
8 64 WWWWWWWl
9 81 WWWWWWWWWh
10 100 WWWWWWWWWWWW

Source blocks

  def main(*args, **kwargs) -> None:
      """
      Example docstring for function
      """
      return

  if __name__ == "__main__":
      main()

Example prose

AMONG the many valuable contributions of William Dwight Whitney to linguistic science is one especially important and fundamental principle. It may be stated in these words. In explaining the prehistoric phenomena of language we must assume no other factors than those which we are able to observe and estimate in the historical period of language development. The factors that produced changes in human speech five thousand or ten thousand years ago cannot have been essentially different from those which are now operating to transform living languages. On the basis of this principle we look to-day at a much-discussed problem of Indo-European philology with views very different from the views held by the founders of Comparative Philology and their immediate successors. I refer to the problem, how the Indo-European people came to assign gender to nouns, to distinguish between masculine, feminine, and neuter. This question is of interest to others besides philologists. What man of culture who has learned languages such as the Greek, Latin, or French has not at times wondered that objects which have no possible connection with the natural gender of animals appear constantly in the language as male or female? In German, for example, it is der fuss, but die hand; der geist, but die seele; in Latin, hīc hortus, hīc animus, hīc amor, but haec planta, haec anima, haec felicitas; in Greek, ὁ πλοῦτος, ὁ οἶκος, but ἡ πενία, ἡ οἰκία.

This gender distinction pervades all the older Indo-European languages, and must therefore be regarded as having its origin in the time of the pro-ethnic Indo-European community. Not only is the subject itself full of interest, but also the treatment it has received from the philological research of our century. The various efforts made to solve the problem may very aptly illustrate an essential difference which exists between the theories of language development held in the beginning and middle of this century and those which prevail to-day, — a difference of method existing not in comparative linguistics alone, but also in other fields of philological and historical research that border on it.