(exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
#+end_src
+** Only one custom theme at a time
+
+#+begin_src emacs-lisp
+(defadvice load-theme (before clear-previous-themes activate)
+ "Clear existing theme settings instead of layering them"
+ (mapc #'disable-theme custom-enabled-themes))
+#+end_src
+
** Server
Start server if not already running. Alternatively, can be done by
'prepend))
#+end_src
+** Libraries
+
+#+begin_src emacs-lisp
+(require 'cl-lib)
+(require 'subr-x)
+#+end_src
+
+** Useful utilities
+
+#+begin_src emacs-lisp
+(defun ab-enlist (exp)
+ "Return EXP wrapped in a list, or as-is if already a list."
+(if (listp exp) exp (list exp)))
+
+; from https://github.com/hlissner/doom-emacs/commit/589108fdb270f24a98ba6209f6955fe41530b3ef
+(defmacro after! (features &rest body)
+ "A smart wrapper around `with-eval-after-load'. Supresses warnings during
+compilation."
+ (declare (indent defun) (debug t))
+ (list (if (or (not (bound-and-true-p byte-compile-current-file))
+ (dolist (next (ab-enlist features))
+ (if (symbolp next)
+ (require next nil :no-error)
+ (load next :no-message :no-error))))
+ #'progn
+ #'with-no-warnings)
+ (cond ((symbolp features)
+ `(eval-after-load ',features '(progn ,@body)))
+ ((and (consp features)
+ (memq (car features) '(:or :any)))
+ `(progn
+ ,@(cl-loop for next in (cdr features)
+ collect `(after! ,next ,@body))))
+ ((and (consp features)
+ (memq (car features) '(:and :all)))
+ (dolist (next (cdr features))
+ (setq body `(after! ,next ,@body)))
+ body)
+ ((listp features)
+ `(after! (:all ,@features) ,@body)))))
+#+end_src
+
* Core
:PROPERTIES:
:CUSTOM_ID: core
'auto-compile-inhibit-compile-detached-git-head))
#+end_src
-*** TODO [[https://github.com/Kungsgeten/ryo-modal][ryo-modal]]
+*** [[https://github.com/noctuid/general.el][general]]
#+begin_quote
Roll your own modal mode
#+end_quote
+#+begin_src emacs-lisp
+(use-package general
+ :demand t
+ :config
+ (general-evil-setup t)
+
+ (general-override-mode)
+
+ (general-create-definer
+ ab--mode-leader-keys
+ :keymaps 'override
+ :states '(emacs normal visual motion insert)
+ :non-normal-prefix "C-,"
+ :prefix ",")
+
+ (general-create-definer
+ ab--leader-keys
+ :keymaps 'override
+ :states '(emacs normal visual motion insert)
+ :non-normal-prefix "M-m"
+ :prefix "SPC"))
+#+end_src
+
+*** evil
+
+#+begin_src emacs-lisp
+(use-package evil
+ :demand t
+ :hook (view-mode . evil-motion-state)
+ :config (evil-mode 1))
+#+end_src
+
+#+begin_src emacs-lisp
+(use-package evil-escape
+ :demand t
+ :init
+ (setq evil-escape-excluded-states '(normal visual multiedit emacs motion)
+ evil-escape-excluded-major-modes '(neotree-mode)
+ evil-escape-key-sequence "jk"
+ evil-escape-delay 0.25)
+ :general
+ (:states '(insert replace visual operator)
+ "C-g" #'evil-escape)
+ :config
+ (evil-escape-mode 1)
+ ;; no `evil-escape' in minibuffer
+ (push #'minibufferp evil-escape-inhibit-functions))
+#+end_src
+
*** [[https://github.com/ch11ng/exwm][EXWM]] (window manager)
#+begin_src emacs-lisp :tangle no
#+begin_src emacs-lisp
(setq org-src-tab-acts-natively t
org-src-preserve-indentation nil
- org-edit-src-content-indentation 0)
+ org-edit-src-content-indentation 0
+ org-html-doctype "html5"
+ org-html-html5-fancy t)
+(add-hook 'org-mode-hook 'org-indent-mode)
+(use-package htmlize)
#+end_src
*** [[https://magit.vc/][Magit]]
#+begin_src emacs-lisp
(use-package magit
+ :general (ab--leader-keys "g s" 'magit-status)
:defer t
:bind (("s-g" . magit-status)
("C-x g" . magit-status)
#+begin_src emacs-lisp
(use-package ivy
+ :defer 1
:bind
(:map ivy-minibuffer-map
([escape] . keyboard-escape-quit)
#+begin_src emacs-lisp
(use-package swiper
+ :general (:states 'normal "/" 'swiper)
:bind (([remap isearch-forward] . swiper)
([remap isearch-backward] . swiper)))
#+end_src
#+begin_src emacs-lisp
(use-package counsel
:defer 1
+ :general (ab--leader-keys
+ "f r" 'counsel-recentf
+ "SPC" 'counsel-M-x
+ "." 'counsel-find-file)
:bind (([remap execute-extended-command] . counsel-M-x)
([remap find-file] . counsel-find-file)
("s-r" . counsel-recentf)
(setq undo-tree-mode-lighter ""))
#+end_src
+* Editing
+
+** Company
+
+#+begin_src emacs-lisp
+(use-package company
+ :defer 5
+ :bind
+ (:map company-active-map
+ ([tab] . company-complete-common-or-cycle))
+ :custom
+ (company-idle-delay 0.3)
+ (company-minimum-prefix-length 1)
+ (company-selection-wrap-around t)
+ (company-dabbrev-char-regexp "\\sw\\|\\s_\\|[-_]")
+ :config
+ (global-company-mode t))
+#+end_src
+
+** Customizations
+
+#+begin_src emacs-lisp
+(ab--leader-keys
+ "b s" 'save-buffer
+ "b b" 'ivy-switch-buffer
+ "," 'ivy-switch-buffer
+ "b k" 'kill-this-buffer
+ "q q" 'evil-save-and-quit)
+#+end_src
+
* Syntax and spell checking
#+begin_src emacs-lisp
(use-package flycheck
#+end_src
* Programming modes
+** Alloy
+
+#+begin_src emacs-lisp
+(use-package alloy-mode)
+#+end_src
+
** [[https://github.com/leanprover/lean-mode][Lean]]
#+begin_src emacs-lisp
flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
#+end_src
+*** [[https://github.com/jyp/dante][dante]]
+
+#+begin_src emacs-lisp
+(use-package dante
+ :after haskell-mode
+ :commands dante-mode
+ :hook (haskell-mode . dante-mode))
+#+end_src
+
*** [[https://github.com/mpickering/hlint-refactor-mode][hlint-refactor]]
Emacs bindings for [[https://github.com/ndmitchell/hlint][hlint]]'s refactor option. This requires the refact
:defer 1
:config (which-key-mode))
#+end_src
+* Email
+** notmuch
+
+#+begin_src emacs-lisp
+(defun ab/notmuch ()
+ "Delete other windows, then launch `notmuch'."
+ (interactive)
+ (require 'notmuch)
+ (delete-other-windows)
+ (notmuch))
+
+;; (ab--leader-keys
+;; "m" 'ab/notmuch
+;; "s" 'save-buffer
+;; "SPC" 'counsel-M-x)
+
+;; (map!
+;; :leader
+;; :desc "notmuch" :n "m" #'ab/notmuch
+;; (:desc "search" :prefix "/"
+;; :desc "notmuch" :n "m" #'counsel-notmuch))
+#+end_src
+
+#+begin_src emacs-lisp
+(defvar ab-maildir "~/mail")
+
+(use-package sendmail
+ ;; :ensure nil
+ :config
+ (setq sendmail-program "/usr/bin/msmtp"
+ mail-specify-envelope-from t
+ mail-envelope-from 'header))
+
+(use-package message
+ ;; :ensure nil
+ :config
+ (setq message-kill-buffer-on-exit t
+ message-send-mail-function 'message-send-mail-with-sendmail
+ message-sendmail-envelope-from 'header
+ message-directory "drafts"
+ message-user-fqdn "fencepost.gnu.org")
+ (add-hook 'message-mode-hook
+ (lambda () (setq fill-column 65
+ message-fill-column 65)))
+ (add-hook 'message-mode-hook
+ #'flyspell-mode)
+ ;; (add-hook 'notmuch-message-mode-hook #'+doom-modeline|set-special-modeline)
+ ;; TODO: is there a way to only run this when replying and not composing?
+ (add-hook 'notmuch-message-mode-hook
+ (lambda () (progn
+ (newline)
+ (newline)
+ (forward-line -1)
+ (forward-line -1))))
+ ;; (add-hook 'message-setup-hook
+ ;; #'mml-secure-message-sign-pgpmime)
+ )
+
+(after! mml-sec
+ (setq mml-secure-openpgp-encrypt-to-self t
+ mml-secure-openpgp-sign-with-sender t))
+
+(use-package notmuch
+ :general (ab--leader-keys "m" 'ab/notmuch)
+ :config
+ (setq notmuch-hello-sections
+ '(notmuch-hello-insert-header
+ notmuch-hello-insert-saved-searches
+ ;; notmuch-hello-insert-search
+ notmuch-hello-insert-alltags)
+ notmuch-search-oldest-first nil
+ notmuch-show-all-tags-list t
+ notmuch-hello-thousands-separator ","
+ notmuch-fcc-dirs
+ '(("amin@aminb.org" . "amin/Sent")
+ ("abandali@uwaterloo.ca" . "\"uwaterloo/Sent Items\"")
+ ("amin.bandali@uwaterloo.ca" . "\"uwaterloo/Sent Items\"")
+ ("aminb@gnu.org" . "gnu/Sent")
+ (".*" . "sent")))
+ ;; (add-hook 'visual-fill-column-mode-hook
+ ;; (lambda ()
+ ;; (when (string= major-mode 'notmuch-message-mode)
+ ;; (setq visual-fill-column-width 70))))
+ ;; (set! :evil-state 'notmuch-message-mode 'insert)
+ ;; (advice-add #'notmuch-bury-or-kill-this-buffer
+ ;; :override #'kill-this-buffer)
+ :bind
+ (:map notmuch-hello-mode-map
+ ("g" . notmuch-poll-and-refresh-this-buffer)
+ ("i" . (lambda ()
+ "Search for `inbox' tagged messages"
+ (interactive)
+ (notmuch-hello-search "tag:inbox")))
+ ("u" . (lambda ()
+ "Search for `unread' tagged messages"
+ (interactive)
+ (notmuch-hello-search "tag:unread")))
+ ("M" . (lambda ()
+ "Compose new mail and prompt for sender"
+ (interactive)
+ (let ((current-prefix-arg t))
+ (call-interactively #'notmuch-mua-new-mail)))))
+ (:map notmuch-search-mode-map
+ ("g" . notmuch-poll-and-refresh-this-buffer)
+ ("k" . (lambda ()
+ "Mark message read"
+ (interactive)
+ (notmuch-search-tag '("-unread"))
+ ;; (notmuch-search-archive-thread)
+ (notmuch-search-next-thread)))
+ ("u" . (lambda ()
+ "Mark message unread"
+ (interactive)
+ (notmuch-search-tag '("+unread"))
+ (notmuch-search-next-thread)))
+ ("K" . (lambda ()
+ "Mark message deleted"
+ (interactive)
+ (notmuch-search-tag '("-unread" "-inbox" "+deleted"))
+ (notmuch-search-archive-thread)))
+ ("S" . (lambda ()
+ "Mark message as spam"
+ (interactive)
+ (notmuch-search-tag '("-unread" "-inbox" "-webmasters" "+spam"))
+ (notmuch-search-archive-thread))))
+ (:map notmuch-tree-mode-map ; TODO: additional bindings
+ ("S" . (lambda ()
+ "Mark message as spam"
+ (interactive)
+ (notmuch-tree-tag '("-unread" "-inbox" "-webmasters" "+spam"))
+ (notmuch-tree-archive-thread))))
+)
+
+;; (use-package counsel-notmuch
+;; :commands counsel-notmuch)
+
+(after! notmuch-crypto
+ (setq notmuch-crypto-process-mime t))
+
+;; (after! evil
+;; (mapc (lambda (str) (evil-set-initial-state (car str) (cdr str)))
+;; '((notmuch-hello-mode . emacs)
+;; (notmuch-search-mode . emacs)
+;; (notmuch-tree-mode . emacs))))
+
+(after! recentf
+ (add-to-list 'recentf-exclude (expand-file-name ab-maildir)))
+#+end_src
* Post initialization
:PROPERTIES: