** Naming conventions
-The conventions below were inspired by [[https://github.com/hlissner/doom-emacs][Doom]]'s conventions, found
-[[https://github.com/hlissner/doom-emacs/blob/5dacbb7cb1c6ac246a9ccd15e6c4290def67757c/core/core.el#L3-L17][here]]. Naturally, I use my initials, =ab=, instead of =doom=.
+The conventions below were inspired by [[https://github.com/hlissner/doom-emacs][Doom]]'s, found [[https://github.com/hlissner/doom-emacs/blob/5dacbb7cb1c6ac246a9ccd15e6c4290def67757c/core/core.el#L3-L17][here]].
#+begin_src emacs-lisp :comments none
;; Naming conventions:
;;
-;; ab-... public variables or non-interactive functions
-;; ab--... private anything (non-interactive), not safe for direct use
-;; ab/... an interactive function; safe for M-x or keybinding
-;; ab:... an evil operator, motion, or command
-;; ab|... a hook function
-;; ab*... an advising function
-;; ab@... a hydra command
-;; ...! a macro
+;; amin-... public variables or non-interactive functions
+;; amin--... private anything (non-interactive), not safe for direct use
+;; amin/... an interactive function; safe for M-x or keybinding
+;; amin:... an evil operator, motion, or command
+;; amin|... a hook function
+;; amin*... an advising function
+;; amin@... a hydra command
+;; ...! a macro
#+end_src
* Initial setup
=init.el=, i.e. =user-init-file=:
#+begin_src emacs-lisp
-(defvar ab--before-user-init-time (current-time)
+(defvar amin--before-user-init-time (current-time)
"Value of `current-time' when Emacs begins loading `user-init-file'.")
(message "Loading Emacs...done (%.3fs)"
- (float-time (time-subtract ab--before-user-init-time
+ (float-time (time-subtract amin--before-user-init-time
before-init-time)))
#+end_src
startup time as well.
#+begin_src emacs-lisp
-(defvar ab--gc-cons-threshold gc-cons-threshold)
-(defvar ab--gc-cons-percentage gc-cons-percentage)
-(defvar ab--file-name-handler-alist file-name-handler-alist)
+(defvar amin--gc-cons-threshold gc-cons-threshold)
+(defvar amin--gc-cons-percentage gc-cons-percentage)
+(defvar amin--file-name-handler-alist file-name-handler-alist)
(setq gc-cons-threshold (* 400 1024 1024) ; 400 MiB
gc-cons-percentage 0.6
file-name-handler-alist nil
(add-hook
'after-init-hook
(lambda ()
- (setq gc-cons-threshold ab--gc-cons-threshold
- gc-cons-percentage ab--gc-cons-percentage
- file-name-handler-alist ab--file-name-handler-alist)))
+ (setq gc-cons-threshold amin--gc-cons-threshold
+ gc-cons-percentage amin--gc-cons-percentage
+ file-name-handler-alist amin--file-name-handler-alist)))
#+end_src
Increase the number of lines kept in message logs (the =*Messages*=
;; '(not free-vars unresolved noruntime lexical make-local))
#+end_src
+** whoami
+
+#+begin_src emacs-lisp
+(setq user-full-name "Amin Bandali"
+ user-mail-address "amin@aminb.org")
+#+end_src
+
** Package management
*** No =package.el=
#+begin_src emacs-lisp
(require 'use-package)
-(if nil ; set to t when need to debug init
+(if nil ; set to t when need to debug init
(setq use-package-verbose t
use-package-expand-minimally nil
use-package-compute-statistics t
** Useful utilities
#+begin_src emacs-lisp
-(defun ab-enlist (exp)
+(defun amin-enlist (exp)
"Return EXP wrapped in a list, or as-is if already a list."
(if (listp exp) exp (list exp)))
compilation."
(declare (indent defun) (debug t))
(list (if (or (not (bound-and-true-p byte-compile-current-file))
- (dolist (next (ab-enlist features))
+ (dolist (next (amin-enlist features))
(if (symbolp next)
(require next nil :no-error)
(load next :no-message :no-error))))
(setq body `(after! ,next ,@body)))
body)
((listp features)
- `(after! (:all ,@features) ,@body)))))
+ `(after! (:all ,@features) ,@body)))))
#+end_src
* Core
using the Xfce panel anymore. Also, I don't need to see the load
average on a regular basis, so disable that.
-#+begin_src emacs-lisp
+Note: using =i3status= on sway at the moment, so disabling this.
+
+#+begin_src emacs-lisp :tangle no
(use-package time
:ensure nil
:init
version-control t)
#+end_src
+*** Auto revert
+
+Enable automatic reloading of changed buffers and files.
+
+#+begin_src emacs-lisp
+(global-auto-revert-mode 1)
+(setq auto-revert-verbose nil
+ global-auto-revert-non-file-buffers t)
+#+end_src
+
+*** Always use space for indentation
+
+#+begin_src emacs-lisp
+(setq-default
+ indent-tabs-mode nil
+ require-final-newline t
+ tab-width 4)
+#+end_src
+
** Packages
The packages in this section are absolutely essential to my everyday
(auto-compile-on-load-mode)
(auto-compile-on-save-mode)
(setq auto-compile-display-buffer nil
- auto-compile-mode-line-counter t
- auto-compile-source-recreate-deletes-dest t
- auto-compile-toggle-deletes-nonlib-dest t
- auto-compile-update-autoloads t)
+ auto-compile-mode-line-counter t
+ auto-compile-source-recreate-deletes-dest t
+ auto-compile-toggle-deletes-nonlib-dest t
+ auto-compile-update-autoloads t)
(add-hook 'auto-compile-inhibit-compile-hook
'auto-compile-inhibit-compile-detached-git-head))
#+end_src
*** [[https://github.com/noctuid/general.el][general]]
-#+begin_quote
-Roll your own modal mode
-#+end_quote
-
-#+begin_src emacs-lisp
+ #+begin_src emacs-lisp
(use-package general
:demand t
:config
(general-override-mode)
(general-create-definer
- ab--mode-leader-keys
+ amin--mode-leader-keys
:keymaps 'override
:states '(emacs normal visual motion insert)
:non-normal-prefix "C-,"
:prefix ",")
(general-create-definer
- ab--leader-keys
+ amin--leader-keys
:keymaps 'override
:states '(emacs normal visual motion insert)
:non-normal-prefix "M-m"
#+begin_src emacs-lisp
(use-package evil
:demand t
- :hook (view-mode . evil-motion-state)
- :config (evil-mode 1))
+ :hook ((view-mode . evil-motion-state)
+ (org-src-mode . evil-insert-state))
+ :init
+ (setq evil-want-integration nil)
+ :config (evil-mode 1)
+ (general-swap-key nil '(normal motion) ";" ":")
+ (setq evil-want-visual-char-semi-exclusive t))
+#+end_src
+
+#+begin_src emacs-lisp
+(use-package evil-collection
+ :after evil
+ :config
+ (evil-collection-init))
#+end_src
#+begin_src emacs-lisp
:general
(:states '(insert replace visual operator)
"C-g" #'evil-escape)
- :config
+ :config
(evil-escape-mode 1)
;; no `evil-escape' in minibuffer
(push #'minibufferp evil-escape-inhibit-functions))
#+end_src
+#+begin_src emacs-lisp
+(use-package evil-magit
+ :after magit
+ :config
+ (evil-set-initial-state 'git-commit-mode 'insert))
+#+end_src
+
+#+begin_src emacs-lisp
+(use-package evil-org
+ :hook (org-mode . evil-org-mode)
+ :general
+ (nvmap evil-org-mode-map
+ "TAB" 'org-cycle
+ "S-TAB" 'org-cycle)
+ :config
+ (evil-org-set-key-theme)
+ (require 'evil-org-agenda)
+ (evil-org-agenda-set-keys)
+ (nmap evil-org-mode-map
+ [backtab] 'org-shifttab)
+ (after! org-src
+ (define-key org-src-mode-map [remap evil-write] 'org-edit-src-save)
+ (define-key org-src-mode-map [remap evil-save-and-close]
+ (lambda () (interactive)
+ (org-edit-src-save)
+ (org-edit-src-exit)))
+ (define-key org-src-mode-map [remap evil-save-modified-and-close]
+ (lambda () (interactive)
+ (org-edit-src-save)
+ (org-edit-src-exit)))))
+#+end_src
+
+#+begin_src emacs-lisp
+(amin--leader-keys
+ "/" '(:ignore t :wk "search")
+
+ "B" '(:ignore t :wk "Borg")
+ "BA" 'borg-activate
+ "Ba" 'borg-assimilate
+ "Bb" 'borg-build
+ "Bc" 'borg-clone
+ "Bd" 'epkg-describe-package
+ "Bp" 'epkg-list-packages
+ "Br" 'borg-remove
+ "Bu" 'epkg-update
+
+ "b" '(:ignore t :wk "buffers")
+ "bb" 'ibuffer-list-buffers
+ "bk" 'kill-this-buffer
+ "bs" 'save-buffer
+
+ "f" '(:ignore t :wk "files")
+
+ "h" '(:ignore t :wk "help")
+ "hc" 'describe-char
+ "hf" 'describe-function
+ "hF" 'describe-face
+ "hi" 'info
+ "hk" 'describe-key
+ "hl" 'view-lossage
+ "hv" 'describe-variable
+
+ "o" 'other-window
+
+ "w" '(:ignore t :wk "window")
+ "wo" 'other-window
+
+ "q" '(:ignore t :wk "quit")
+ "qq" 'save-buffers-kill-terminal)
+#+end_src
+
*** [[https://orgmode.org/][Org mode]]
#+begin_quote
In short, my favourite way of life.
#+begin_src emacs-lisp
-(setq org-src-tab-acts-natively t
- org-src-preserve-indentation nil
- org-edit-src-content-indentation 0
- org-html-divs '((preamble "header" "preamble")
- (content "main" "content")
- (postamble "footer" "postamble"))
- org-html-doctype "html5"
- org-html-html5-fancy t
- org-html-postamble nil)
-(add-hook 'org-mode-hook 'org-indent-mode)
-(use-package htmlize)
+(use-package org
+ :general
+ (amin--leader-keys "bt" 'org-babel-tangle)
+ (amin--leader-keys org-mode-map "i" 'org-edit-special)
+ :config
+ (setq org-src-tab-acts-natively t
+ org-src-preserve-indentation nil
+ org-edit-src-content-indentation 0)
+ :hook (org-mode . org-indent-mode))
+
(use-package org-notmuch
:after (:any org notmuch))
#+end_src
#+begin_src emacs-lisp
(use-package magit
- :general (ab--leader-keys "g s" 'magit-status)
+ :general
+ (amin--leader-keys
+ "g" '(:ignore t :wk "magit")
+ "g SPC" 'magit-status
+ "gs" 'magit-status
+ "gS" 'magit-status-prefix
+ "gB" 'magit-blame
+ "gC" 'magit-clone
+ "gf" 'magit-fetch
+ "gF" 'magit-pull
+ "gP" 'magit-push
+ "gcc" 'magit-commit
+ "gca" 'magit-commit-amend
+ "gbb" 'magit-checkout
+ "gbc" 'magit-branch)
:defer t
:bind (("s-g" . magit-status)
- ("C-x g" . magit-status)
+ ("C-x g" . magit-status)
("C-x M-g" . magit-dispatch-popup))
:config
(magit-add-section-hook 'magit-status-sections-hook
([S-up] . ivy-previous-history-element)
([S-down] . ivy-next-history-element)
("DEL" . ivy-backward-delete-char))
+ :general (amin--leader-keys "," 'ivy-switch-buffer)
:config
(setq ivy-wrap t)
(ivy-mode 1))
#+begin_src emacs-lisp
(use-package swiper
- :general (:states 'normal "/" 'swiper)
+ :general (:states '(normal motion) "/" 'swiper)
+ (amin--leader-keys "//" 'swiper)
:bind (([remap isearch-forward] . swiper)
- ([remap isearch-backward] . swiper)))
+ ([remap isearch-backward] . swiper)))
#+end_src
**** Counsel
#+begin_src emacs-lisp
(use-package counsel
:defer 1
- :general (ab--leader-keys
- "f r" 'counsel-recentf
- "SPC" 'counsel-M-x
- "." 'counsel-find-file)
+ :general
+ (amin--leader-keys
+ "fr" '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)
:map minibuffer-local-map
- ("C-r" . counsel-minibuffer-history))
+ ("C-r" . counsel-minibuffer-history))
:config
(counsel-mode 1)
(defalias 'locate #'counsel-locate))
(list (regexp-quote (system-name)) nil nil)))
(use-package undo-tree
+ :bind (("C-?" . undo-tree-undo)
+ ("M-_" . undo-tree-redo))
:config
(global-undo-tree-mode)
- (setq undo-tree-mode-lighter ""))
+ (setq undo-tree-mode-lighter ""
+ undo-tree-auto-save-history t))
#+end_src
* Editing
:defer 5
:bind
(:map company-active-map
- ([tab] . company-complete-common-or-cycle))
+ ([tab] . company-complete-common-or-cycle))
:custom
(company-idle-delay 0.3)
(company-minimum-prefix-length 1)
(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
#+begin_src emacs-lisp
(use-package lean-mode
:bind (:map lean-mode-map
- ("S-SPC" . company-complete)))
+ ("S-SPC" . company-complete)))
#+end_src
** Haskell
:config
(setq haskell-indentation-layout-offset 4
haskell-indentation-left-offset 4
- flycheck-checker 'haskell-hlint
- flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
+ flycheck-checker 'haskell-hlint
+ flycheck-disabled-checkers '(haskell-stack-ghc haskell-ghc)))
#+end_src
*** [[https://github.com/jyp/dante][dante]]
#+begin_src emacs-lisp
(use-package hlint-refactor
:bind (:map hlint-refactor-mode-map
- ("C-c l b" . hlint-refactor-refactor-buffer)
- ("C-c l r" . hlint-refactor-refactor-at-point))
+ ("C-c l b" . hlint-refactor-refactor-buffer)
+ ("C-c l r" . hlint-refactor-refactor-at-point))
:hook (haskell-mode . hlint-refactor-mode))
#+end_src
(new-old-code ""))
(save-excursion
(switch-to-buffer (get-file-buffer fname))
- (goto-char (point-min))
- (forward-line (1- fline))
+ (goto-char (point-min))
+ (forward-line (1- fline))
(beginning-of-line)
(setf bline (point))
(when (or hs-lint-replace-without-ask
(load-theme 'eink t)
#+end_src
-* Email
-** [[https://notmuchmail.org][notmuch]]
-
-See [[notmuch:id:87muuqsvci.fsf@fencepost.gnu.org][bug follow-up]].
+** [[https://github.com/bbatsov/crux][crux]]
#+begin_src emacs-lisp
-(defun ab/notmuch ()
- "Delete other windows, then launch `notmuch'."
- (interactive)
- (require 'notmuch)
- (delete-other-windows)
- (notmuch))
+(use-package crux
+ :bind (("C-c d" . crux-duplicate-current-line-or-region)
+ ("C-c M-d" . crux-duplicate-and-comment-current-line-or-region))
+ :general
+ (amin--leader-keys
+ "bK" 'crux-kill-other-buffers
+ "fc" 'crux-copy-file-preserve-attributes
+ "fD" 'crux-delete-file-and-buffer
+ "fR" 'crux-rename-file-and-buffer))
+#+end_src
-;; (ab--leader-keys
-;; "m" 'ab/notmuch
-;; "s" 'save-buffer
-;; "SPC" 'counsel-M-x)
+** [[https://github.com/alezost/mwim.el][mwim]]
-;; (map!
-;; :leader
-;; :desc "notmuch" :n "m" #'ab/notmuch
-;; (:desc "search" :prefix "/"
-;; :desc "notmuch" :n "m" #'counsel-notmuch))
+#+begin_src emacs-lisp
+(use-package mwim
+ :bind (("C-a" . mwim-beginning-of-code-or-line)
+ ("C-e" . mwim-end-of-code-or-line)
+ ("<home>" . mwim-beginning-of-line-or-code)
+ ("<end>" . mwim-end-of-line-or-code))
+ :general (:states '(normal visual)
+ "0" 'mwim-beginning-of-code-or-line
+ "$" 'mwim-end-of-code-or-line))
#+end_src
+* Email
+** [[https://notmuchmail.org][notmuch]]
+
+See [[notmuch:id:87muuqsvci.fsf@fencepost.gnu.org][bug follow-up]].
+
#+begin_src emacs-lisp
-(defvar ab-maildir "~/mail")
+(defvar amin-maildir "~/mail")
(use-package sendmail
;; :ensure nil
:config
(setq sendmail-program "/usr/bin/msmtp"
+ ; message-sendmail-extra-arguments '("-v" "-d")
mail-specify-envelope-from t
mail-envelope-from 'header))
message-send-mail-function 'message-send-mail-with-sendmail
message-sendmail-envelope-from 'header
message-directory "drafts"
- message-user-fqdn "fencepost.gnu.org")
+ message-user-fqdn "aminb.org")
(add-hook 'message-mode-hook
(lambda () (setq fill-column 65
message-fill-column 65)))
(setq mml-secure-openpgp-encrypt-to-self t
mml-secure-openpgp-sign-with-sender t))
+(defun amin/notmuch ()
+ "Delete other windows, then launch `notmuch'."
+ (interactive)
+ (delete-other-windows)
+ (notmuch))
+
(use-package notmuch
- :general (ab--leader-keys "m" 'ab/notmuch)
+ :general (amin--leader-keys "m" 'amin/notmuch)
+ :commands notmuch
:config
(setq notmuch-hello-sections
'(notmuch-hello-insert-header
'("Subject" "To" "Cc" "Date" "List-Id" "X-RT-Originator")
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"))
+ '(("amin@aminb.org" . "amin/Sent")
+ ("amin@gnu.org" . "gnu/Sent")
+ ("abandali@uwaterloo.ca" . "\"uwaterloo/Sent Items\"")
+ ("mab@gnu.org" . "gnu/Sent")
+ ("aminb@gnu.org" . "gnu/Sent")
+ (".*" . "sent"))
notmuch-search-result-format
- '(("date" . "%12s ")
- ("count" . "%-7s ")
+ '(("date" . "%12s ")
+ ("count" . "%-7s ")
("authors" . "%-40s ")
("subject" . "%s ")
- ("tags" . "(%s)")))
+ ("tags" . "(%s)"))
+ notmuch-saved-searches
+ '((:name "inbox" :query "tag:inbox" :key "i")
+ (:name "unread" :query "tag:unread" :key "u")
+ (:name "latest" :query "tag:latest" :key "l")
+ (:name "encrypted" :query "tag:encrypted" :key "e")
+ (:name "flagged" :query "tag:flagged" :key "f")
+ (:name "sent" :query "tag:sent" :key "s")
+ (:name "drafts" :query "tag:draft" :key "d")
+ (:name "all mail" :query "*" :key "a")))
;; (add-hook 'visual-fill-column-mode-hook
;; (lambda ()
;; (when (string= major-mode 'notmuch-message-mode)
;; (set! :evil-state 'notmuch-message-mode 'insert)
;; (advice-add #'notmuch-bury-or-kill-this-buffer
;; :override #'kill-this-buffer)
+ (evil-collection-define-key 'normal 'notmuch-common-keymap
+ "c" (lambda ()
+ "Compose new mail and prompt for sender"
+ (interactive)
+ (let ((current-prefix-arg t))
+ (call-interactively #'notmuch-mua-new-mail))))
: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")))
- ("l" . (lambda ()
- "Search for `latest tagged messages"
- (interactive)
- (notmuch-hello-search "tag:latest")))
- ("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))))
+ ("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))))
+ ("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)
+(use-package counsel-notmuch
+ :general (amin--leader-keys "/m" 'counsel-notmuch))
(after! notmuch-crypto
(setq notmuch-crypto-process-mime t))
;; (notmuch-tree-mode . emacs))))
(after! recentf
- (add-to-list 'recentf-exclude (expand-file-name ab-maildir)))
+ (add-to-list 'recentf-exclude (expand-file-name amin-maildir)))
#+end_src
** supercite
#+begin_src emacs-lisp
(message "Loading %s...done (%.3fs)" user-init-file
(float-time (time-subtract (current-time)
- ab--before-user-init-time)))
+ amin--before-user-init-time)))
#+end_src
* Footer