emacs/README.org
Evie Litherland-Smith 064a358239 Switch to managing packages via nix
Remove all instances of adding to package-selected-packages

Remove package config and custom ensure function

Remove Makefile and install.el
2024-12-02 16:38:43 +00:00

1579 lines
53 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

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

#+title: Emacs Config
#+author: Evie Litherland-Smith
#+email: evie@xenia.me.uk
#+property: header-args:emacs-lisp :results silent
Personal Emacs configuration. Clone to =~/.emacs.d/= and install
specified plugins using [[file:install.el][the install script]].
* Config
** Package management
Customise =use-package= before first time it's used.
#+begin_src emacs-lisp
(setopt use-package-check-before-init t
use-package-enable-imenu-support t)
#+end_src
** Delight for minor-modes
Install =delight= to hide certain minor-modes from modeline
#+begin_src emacs-lisp
(use-package delight
:demand t
:if (package-installed-p 'delight))
#+end_src
** Defaults
Set some useful defaults. Some of these should be moved to relevant
section of configuration.
#+begin_src emacs-lisp
(setq user-full-name "Evie Litherland-Smith"
user-mail-address "evie@xenia.me.uk"
custom-file (locate-user-emacs-file "custom.el")
use-short-answers t
kill-do-not-save-duplicates t)
(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8)
(global-auto-revert-mode +1)
(delete-selection-mode +1)
#+end_src
Filter warning messages from causing popups. Keep in log, but only
show for error or emergency.
#+begin_src emacs-lisp
(setq warning-minimum-level :error)
#+end_src
Bind mouse keys to expected movement commands
#+begin_src emacs-lisp
(keymap-global-set "<mouse-8>" #'previous-buffer)
(keymap-global-set "<mouse-9>" #'next-buffer)
#+end_src
Set custom location for backups
#+begin_src emacs-lisp
(setq backup-directory-alist '(("." . "~/.local/state/emacs/backups")))
#+end_src
** Secrets
#+begin_src emacs-lisp
(use-package secrets)
#+end_src
** UI and Appearance
Configure the look and feel of Emacs
#+begin_src emacs-lisp
(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
tab-bar-show 1
tab-line-tabs-function 'tab-line-tabs-mode-buffers)
(global-prettify-symbols-mode +1)
(global-tab-line-mode +1)
(menu-bar-mode +1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
#+end_src
*** Nerd Icons
#+begin_src emacs-lisp
(use-package nerd-icons
:if (package-installed-p 'nerd-icons))
(use-package nerd-icons-dired
:if (package-installed-p 'nerd-icons-ibuffer)
:after (nerd-icons dired)
:commands nerd-icons-dired-mode
:hook (dired-mode . (lambda () (nerd-icons-dired-mode +1))))
(use-package nerd-icons-ibuffer
:if (package-installed-p 'nerd-icons-ibuffer)
:after nerd-icons
:commands nerd-icons-ibuffer-mode
:hook (ibuffer-mode . (lambda () (nerd-icons-ibuffer-mode +1))))
(use-package nerd-icons-corfu
:if (package-installed-p 'nerd-icons-corfu)
:after nerd-icons)
#+end_src
*** Modeline
#+begin_src emacs-lisp
(setopt mode-line-compact 'long)
(line-number-mode +1)
(column-number-mode +1)
(size-indication-mode -1)
#+end_src
*** Visual fill column
#+begin_src emacs-lisp
(use-package visual-fill-column
:if (package-installed-p 'visual-fill-column)
:functions (visual-fill-column-mode)
:hook ((prog-mode . (lambda ()
(visual-line-mode +1)
(visual-fill-column-mode +1)))
((Info-mode man-common) . (lambda ()
(setq-local visual-fill-column-width 80)
(visual-line-mode +1)
(visual-fill-column-mode +1))))
:custom
(visual-fill-column-width 120)
(visual-fill-column-center-text t)
(visual-fill-column-enable-sensible-window-split t)
:config
(setopt visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow)))
(with-eval-after-load 'mu4e
(add-hook 'mu4e-view-mode-hook
#'(lambda ()
(visual-line-mode +1)
(visual-fill-column-mode +1)))
(add-hook 'mu4e-compose-mode-hook
#'(lambda ()
(visual-line-mode +1)
(visual-fill-column-mode +1))))
#+end_src
*** Font ligatures
#+begin_src emacs-lisp
(use-package ligature
:load-path "external-packages/ligature.el/"
:functions (ligature-set-ligatures
global-ligature-mode)
:config
(ligature-set-ligatures
'(prog-mode)
'(("<" (rx (= 1 "=")))
(">" (rx (= 1 "=")))
("-" (rx (* "-") (= 1 ">")))
("=" (rx (* "=") (? ">")))
("!" (rx (+ "=")))))
(global-ligature-mode +1))
#+end_src
*** Font Showcase
This is a showcase of various font and UI features to act as a
standard candle.
**** Font emphasis
Examples of:
- *Bold text*
- /Italic text/
- _Underscored text_
- =Literal text=
- ~Code~
- +Strike-through+
**** Character showcase
#+begin_example
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 ſßðþ ΓΔΛαδιλμξπτχ∂ ЖЗКУЯжзклмнруфчьыя <= != == => ->
#+end_example
***** 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 |
#+TBLFM: $2=$1**2::$3='(orgtbl-ascii-draw $2 1 100 12)
**** Source blocks
#+begin_src python
def main(*args, **kwargs) -> None:
"""
Example docstring for function
"""
return
if __name__ == "__main__":
main()
#+end_src
**** Example prose
#+begin_quote
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.
#+end_quote
** Buffer and Window management
Rules and packages for buffer management and window navigation.
#+begin_src emacs-lisp
(setq split-height-threshold nil
split-width-threshold 160)
#+end_src
** Completion
#+begin_src emacs-lisp
(setq tab-always-indent 'complete
completion-cycle-threshold nil
completions-detailed t)
#+end_src
** Org Mode
** Development Tools
*** Directory shortcuts
#+begin_src emacs-lisp
(add-to-list 'directory-abbrev-alist
'("^/ndrive" . "/smb:elitherl%ccfepc@msrv-cfshare.ccfepc.ccfe.ac.uk:/NDrive/"))
(add-to-list 'directory-abbrev-alist
'("^/tdrive" . "/smb:elitherl%ccfepc@msrv-cfshare.ccfepc.ccfe.ac.uk:/NewT/"))
#+end_src
*** TRAMP
#+begin_src emacs-lisp
(use-package tramp
:defer t
:custom
(tramp-default-method "scp")
(tramp-backup-directory-alist backup-directory-alist)
(tramp-auto-save-directory (cdr (assoc "." tramp-backup-directory-alist)))
(remote-file-name-inhibit-cache 300)
(remote-file-name-inhibit-locks t)
:config
(add-to-list 'tramp-remote-path 'tramp-own-remote-path)
(add-to-list 'tramp-remote-path "~/.local/bin/")
(add-to-list 'tramp-remote-path "~/bin")
(add-to-list 'tramp-default-proxies-alist
'("legion" "\\`root\\'" "/ssh:%h:")))
(connection-local-set-profile-variables
'remote-no-corfu-auto
'((corfu-auto . nil)))
(connection-local-set-profiles
'(:application tramp)
'remote-no-corfu-auto)
#+end_src
** Calendar, Email and Messaging
*** Calendar and Appointments
#+begin_src emacs-lisp
(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))))))
(use-package mu4e-icalendar
:after (mu4e org-agenda)
:functions (mu4e-icalendar-setup)
:custom
(mu4e-icalendar-trash-after-reply nil)
:config
(mu4e-icalendar-setup))
#+end_src
*** Khalel
#+begin_src emacs-lisp
(use-package khalel
:if (package-installed-p 'khalel)
:after (org-agenda)
:demand
:commands (khalel-add-capture-template khalel-import-events)
:custom
(khalel-default-alarm "10")
(khalel-default-calendar nil)
(khalel-import-org-file (expand-file-name "calendar.org" org-directory))
(khalel-import-org-file-read-only nil)
(khalel-import-org-file-confirm-overwrite nil)
(khalel-import-start-date "-365d")
(khalel-import-end-date "+365d")
(khalel-import-org-file-header "#+TITLE: khalel imported calendar events\n#+COLUMNS: %ITEM %TIMESTAMP %LOCATION %CALENDAR\n#+CATEGORY: Calendar\n\n")
:config
(khalel-import-events)
(khalel-add-capture-template))
#+end_src
*** MU4E
Configure email with iCalendar event support, to integrate with
=org-agenda=.
#+begin_src emacs-lisp
(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)
(require 'mu4e-icalendar)
(require 'khalel-icalendar))
(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")
(khalel-default-calendar . "personal")
(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"
"Email: e.litherlandsmith@proton.me\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")
(khalel-default-calendar . "other")
(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"
"Email: e.litherlandsmith@icloud.com\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")
(khalel-default-calendar . "work")
(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:" . "󰪲 :")
mu4e-modeline-all-clear '("C:" . "󰪲 :")
mu4e-modeline-new-items '("N:" . "󰧬 :")
mu4e-modeline-unread-items '("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))
#+end_src
*** IRC
#+begin_src emacs-lisp
(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 'secrets)
(erc-tls
:server "irc.libera.chat"
:password (secrets-get-secret "default" "IRC libera.chat")))
#+end_src
** Other
** Initial copy from =init.el=
#+begin_src emacs-lisp
(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))
;; 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)
(use-package savehist
:demand
:config
(savehist-mode +1))
(use-package dired
:hook (dired-mode . (lambda () (dired-omit-mode +1)))
:custom
(dired-auto-revert-buffer t)
(dired-dwim-target t)
(dired-omit-files "\\`[.]?#\\|\\`[.][.]?.*\\'"))
(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:default")))
;; 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
:delight flyspell-mode
:hook ((text-mode . flyspell-mode)
(prog-mode . flyspell-prog-mode))
:custom
(flyspell-use-meta-tab nil)
:init
(require 'ispell))
(use-package ibuffer
:defines ibuffer-filter-groups
:bind (("C-c b" . ibuffer)))
(use-package eshell
:bind (("C-c 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))
(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
:hook (prog-mode . (lambda () (electric-pair-local-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)))
(dolist (command '(scroll-up-command
scroll-down-command
recenter-top-bottom other-window))
(advice-add command :after #'pulse-line))
(use-package whitespace
:hook prog-mode
:custom
(whitespace-action nil)
(whitespace-style '(face
trailing
missing-newline-at-eof
empty
indentation
space-after-tab
space-before-tab
tab-mark)))
(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)
:custom
(diff-hl-disable-on-remote nil)
:init
(global-diff-hl-mode)
(add-hook 'magit-pre-refresh-hook #'diff-hl-magit-pre-refresh)
(add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh))
(use-package winner
:demand
:config
(winner-mode))
(use-package ediff
:custom
(ediff-window-setup-function #'ediff-setup-windows-plain))
(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-default-command #'org-archive-to-archive-sibling)
(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
:delight org-indent-mode
: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)
: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-preserve-indentation t)
(org-src-window-setup 'current-window))
(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 n n" . org-capture))
:custom (org-capture-templates
'(("n" "Note" entry
(file+olp "notes.org" "Inbox"))
("t" "Task" entry
(file+olp "tasks.org" "Inbox")
"* TODO %?\nDEADLINE: %t\n %i\n %a")))
:config
(when (package-installed-p 'khalel)
(with-eval-after-load 'khalel
(khalel-add-capture-template))))
(use-package org-roam
:if (package-installed-p 'org-roam)
:after org
:defines org-roam-directory
:functions org-roam-db-autosync-mode
:bind-keymap ("C-c n d" . org-roam-dailies-map)
:bind (("C-c n r" . org-roam-capture)
("C-c n f" . org-roam-node-find)
:map org-mode-map
("C-c n i" . org-roam-node-insert)
("C-c n l" . 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" week)))))
(use-package org-clock
:after org
:custom
(org-clock-rounding-minutes 15)
(org-clock-mode-line-total 'auto))
(use-package org-duration
:after org
:custom
(org-duration-format '((special . h:mm))))
(use-package org-habit
:after org
:custom
(org-habit-show-habits t)
(org-habit-show-habits-only-for-today t))
(use-package org-agenda
:after (org appt)
:bind (("C-c a" . org-agenda))
: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-inhibit-startup t)
(org-agenda-tags-column 0)
(org-agenda-diary-file (expand-file-name "diary.org" 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 '(:maxlevel 6 :emphasize t :stepskip0 t :fileskip0 t :properties ("WON")))
(org-agenda-start-with-log-mode t)
(org-agenda-start-with-clockreport-mode t)
(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 "roam/journal.org" org-directory)))
:config
(appt-activate +1)
(setq org-agenda-custom-commands
'(("p" "Personal Agenda" tags "+personal")
("w" "Work Agenda" tags "+work"))))
(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
:if (package-installed-p 'org-noter)
:disabled t
: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 '("notes.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
:if (package-installed-p '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
:custom
(vertico-cycle t)
:init
(vertico-mode +1)
:config
(require 'vertico-directory))
(use-package marginalia
:if (package-installed-p 'marginalia)
:functions marginalia-mode
:custom
(marginalia-annotators '(marginalia-annotators-heavy
marginalia-annotators-light
nil))
:init
(marginalia-mode +1)
: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 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 ((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 t)
(corfu-auto-delay 0.2)
(corfu-auto-prefix 3)
(corfu-preselect 'valid)
:init
(global-corfu-mode +1)
: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
:after (corfu savehist)
:functions corfu-history
:config
(add-to-list 'savehist-additional-variables #'corfu-history))
(use-package corfu-popupinfo
:after 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
:if (package-installed-p 'corfu-terminal)
:after 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
:if (package-installed-p 'consult-eglot)
:after (consult eglot)
:bind (("C-c c 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)
:after 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 e" . eglot)
("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)))))
:custom
(eglot-menu-string "lsp")
(eglot-send-changes-idle-time 0.5)
(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
'( :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)
:delight apheleia-mode
:defines (apheleia-formatters
apheleia-mode-alist)
:bind (("C-c c f" . apheleia-format-buffer))
:hook (prog-mode)
:custom (apheleia-remote-algorithm 'local)
:config
(add-to-list 'apheleia-mode-alist '(python-ts-mode . (ruff)))
(add-to-list 'apheleia-mode-alist '(python-mode . (ruff))))
(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 0.5)
(flymake-show-diagnostics-at-end-of-line 'short))
(use-package flymake-shellcheck
:if (package-installed-p 'flymake-shellcheck)
:disabled t
:functions flymake-shellcheck-load
:after 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
:after flymake
:hook (yaml-ts-mode . (lambda () (if (executable-find "yamllint" t)
(flymake-yamllint-setup)))))
(use-package flymake-clippy
:if (package-installed-p 'flymake-clippy)
:disabled t
:functions flymake-clippy-setup-backend
:after 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
:after 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")
(project-vc-dir "VC Status")
(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
:if (package-installed-p 'magit)
:bind (("C-c g" . magit-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 forge
:if (package-installed-p 'forge)
:after magit)
(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))
(use-package lua-mode
:if (package-installed-p 'lua-mode)
:mode "\\.lua\\'")
(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-interpreter "python3")
(python-shell-interpreter "python3")
(python-shell-dedicated nil)
(python-shell-completion-native-enable nil)
(python-indent-guess-indent-offset nil)
(python-indent-offset 4)
(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)
:disabled t
:hook python-base-mode)
(use-package files
:custom
(view-read-only t)
(enable-remote-dir-locals t))
(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 nil))
(use-package auctex
:if (package-installed-p 'auctex)
:defer t)
(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 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 scad-mode
:if (package-installed-p 'scad-mode)
:defer t)
;; 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/"))
(defun my/open-projects-directory ()
"Open Projects directory."
(interactive)
(find-file "~/Projects/"))
(keymap-global-set "C-c w d" #'my/open-documents-directory)
(keymap-global-set "C-c w C-d" #'my/open-downloads-directory)
(keymap-global-set "C-c w p" #'my/open-projects-directory)
#+end_src