X-Git-Url: https://git.shemshak.org/~bandali/configs/blobdiff_plain/8e9c7d731c1d20e574261c991d40cf4e13a1d569..2a6e947b63d838082d20b79fbe7886d18586e531:/init.org?ds=sidebyside diff --git a/init.org b/init.org index 8bba36d..e7882d5 100644 --- a/init.org +++ b/init.org @@ -105,20 +105,19 @@ file. ** 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 @@ -137,10 +136,10 @@ let's see how long Emacs takes to start up, before even loading =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 @@ -150,9 +149,9 @@ frequency. Clearing the ~file-name-handler-alist~ seems to help reduce 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 @@ -167,9 +166,9 @@ done initializing. (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*= @@ -188,6 +187,13 @@ but for now I've decided to keep them enabled. See documentation for ;; '(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= @@ -227,6 +233,17 @@ can then be managed with the help of Magit or other tools. (expand-file-name "lib/borg" user-emacs-directory)) (require 'borg) (borg-initialize) + +(require 'borg-nix-shell) +(setq borg-build-shell-command 'borg-nix-shell-build-command) + +(with-eval-after-load 'bind-key + (bind-keys + :package borg + ("C-c B A" . borg-activate) + ("C-c B a" . borg-assimilate) + ("C-c B b" . borg-build) + ("C-c B c" . borg-clone))) #+end_src *** =use-package= @@ -241,7 +258,7 @@ and without compromising on performance. #+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 @@ -262,7 +279,12 @@ database, low-level functions for querying the database, and a #+begin_src emacs-lisp (use-package epkg - :defer t) + :defer t + :bind + (("C-c B d" . epkg-describe-package) + ("C-c B p" . epkg-list-packages) + ("C-c B r" . borg-remove) + ("C-c B u" . epkg-update))) #+end_src ** No littering in =~/.emacs.d= @@ -390,7 +412,7 @@ Font stack with better unicode support, around =Ubuntu Mono= and ** 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))) @@ -400,7 +422,7 @@ Font stack with better unicode support, around =Ubuntu Mono= and 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)))) @@ -419,7 +441,16 @@ compilation." (setq body `(after! ,next ,@body))) body) ((listp features) - `(after! (:all ,@features) ,@body))))) + `(after! (:all ,@features) ,@body))))) +#+end_src + +Convenience macro for =setq='ing multiple variables to the same value: + +#+begin_src emacs-lisp +(defmacro setq-every! (value &rest vars) + "Set all the variables from VARS to value VALUE." + (declare (indent defun) (debug t)) + `(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars))) #+end_src * Core @@ -435,16 +466,16 @@ Enable displaying time and battery in the mode-line, since I'm not 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 (setq display-time-default-load-average nil) :config (display-time-mode)) (use-package battery - :ensure nil :config (display-battery-mode)) #+end_src @@ -555,86 +586,85 @@ variable. #+begin_src emacs-lisp (setq backup-by-copying t - version-control t) + version-control t + delete-old-versions t) #+end_src -** Packages +*** Auto revert -The packages in this section are absolutely essential to my everyday -workflow, and they play key roles in how I do my computing. They -immensely enhance the Emacs experience for me; both using Emacs, and -customizing it. +Enable automatic reloading of changed buffers and files. -*** [[https://github.com/emacscollective/auto-compile][auto-compile]] +#+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 -(use-package auto-compile - :demand t - :config - (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) - (add-hook 'auto-compile-inhibit-compile-hook - 'auto-compile-inhibit-compile-detached-git-head)) +(setq-default + indent-tabs-mode nil + require-final-newline t + tab-width 4) #+end_src -*** [[https://github.com/noctuid/general.el][general]] +*** Winner mode -#+begin_quote -Roll your own modal mode -#+end_quote +Enable =winner-mode=. #+begin_src emacs-lisp -(use-package general - :demand t - :config - (general-evil-setup t) - - (general-override-mode) +(winner-mode 1) +#+end_src - (general-create-definer - ab--mode-leader-keys - :keymaps 'override - :states '(emacs normal visual motion insert) - :non-normal-prefix "C-," - :prefix ",") +*** Close =*compilation*= on success - (general-create-definer - ab--leader-keys - :keymaps 'override - :states '(emacs normal visual motion insert) - :non-normal-prefix "M-m" - :prefix "SPC")) +#+begin_src emacs-lisp +(setq compilation-exit-message-function + (lambda (status code msg) + "Close the compilation window if successful." + ;; if M-x compile exits with 0 + (when (and (eq status 'exit) (zerop code)) + (bury-buffer) + (delete-window (get-buffer-window (get-buffer "*compilation*")))) + ;; return the result of compilation-exit-message-function + (cons msg code))) #+end_src -*** evil +** Bindings #+begin_src emacs-lisp -(use-package evil - :demand t - :hook (view-mode . evil-motion-state) - :config (evil-mode 1)) +(bind-keys + ("C-c b k" . kill-this-buffer) + ("C-c s s" . save-buffer) + ("C-c b s" . save-buffer) + ("C-c S" . save-buffer) + ("C-c o" . other-window) + ("C-c q q" . save-buffers-kill-terminal)) #+end_src +** Packages + +The packages in this section are absolutely essential to my everyday +workflow, and they play key roles in how I do my computing. They +immensely enhance the Emacs experience for me; both using Emacs, and +customizing it. + +*** [[https://github.com/emacscollective/auto-compile][auto-compile]] + #+begin_src emacs-lisp -(use-package evil-escape +(use-package auto-compile :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)) + (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) + (add-hook 'auto-compile-inhibit-compile-hook + 'auto-compile-inhibit-compile-detached-git-head)) #+end_src *** [[https://orgmode.org/][Org mode]] @@ -648,17 +678,63 @@ system. 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 + :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 + +**** asynchronous tangle + +=amin/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's +d/async-babel-tangle]] which uses [[https://github.com/jwiegley/emacs-async][async]] to asynchronously tangle an org +file. + +#+begin_src emacs-lisp +(after! org + (defvar amin-show-async-tangle-results nil + "Keep *emacs* async buffers around for later inspection.") + + (defvar amin-show-async-tangle-time nil + "Show the time spent tangling the file.") + + (defvar amin-async-tangle-post-compile "make ti" + "If non-nil, pass to `compile' after successful tangle.") + + (defun amin/async-babel-tangle () + "Tangle org file asynchronously." + (interactive) + (let* ((file-tangle-start-time (current-time)) + (file (buffer-file-name)) + (file-nodir (file-name-nondirectory file)) + (async-quiet-switch "-q")) + (async-start + `(lambda () + (require 'org) + (org-babel-tangle-file ,file)) + (unless amin-show-async-tangle-results + `(lambda (result) + (if result + (progn + (message "Tangled %s%s" + ,file-nodir + (if amin-show-async-tangle-time + (format " (%.3fs)" + (float-time (time-subtract (current-time) + ',file-tangle-start-time))) + "")) + (when amin-async-tangle-post-compile + (compile amin-async-tangle-post-compile))) + (message "Tangling %s failed" ,file-nodir)))))))) + +(add-to-list + 'safe-local-variable-values + '(eval add-hook 'after-save-hook #'amin/async-babel-tangle 'append 'local)) #+end_src *** [[https://magit.vc/][Magit]] @@ -671,11 +747,25 @@ Not just how I do git, but /the/ way to do git. #+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) - ("C-x M-g" . magit-dispatch-popup)) + :bind + (("s-g" . magit-dispatch-popup) + ("C-x g" . magit-status) + :prefix-map amin--magit-prefix-map + :prefix "C-c g" + ("SPC" . magit-status) + ("s" . magit-status) + ("S" . magit-status-prefix) + ("B" . magit-blame) + ("C" . magit-clone) + ("f" . magit-fetch) + ("F" . magit-pull) + ("P" . magit-push) + ("p" . magit-dispatch-popup) + ("c c" . magit-commit) + ("c a" . magit-commit-amend) + ("b b" . magit-checkout) + ("b c" . magit-branch)) :config (magit-add-section-hook 'magit-status-sections-hook 'magit-insert-modules @@ -698,13 +788,12 @@ There's no way I could top that, so I won't attempt to. (use-package ivy :defer 1 :bind - (:map ivy-minibuffer-map - ([escape] . keyboard-escape-quit) - ;; ("C-j" . ivy-next-line) - ;; ("C-k" . ivy-previous-line) - ([S-up] . ivy-previous-history-element) - ([S-down] . ivy-next-history-element) - ("DEL" . ivy-backward-delete-char)) + (("C-c b b" . ivy-switch-buffer) + :map ivy-minibuffer-map + ([escape] . keyboard-escape-quit) + ([S-up] . ivy-previous-history-element) + ([S-down] . ivy-next-history-element) + ("DEL" . ivy-backward-delete-char)) :config (setq ivy-wrap t) (ivy-mode 1)) @@ -714,9 +803,8 @@ There's no way I could top that, so I won't attempt to. #+begin_src emacs-lisp (use-package swiper - :general (:states 'normal "/" 'swiper) :bind (([remap isearch-forward] . swiper) - ([remap isearch-backward] . swiper))) + ([remap isearch-backward] . swiper))) #+end_src **** Counsel @@ -724,20 +812,104 @@ There's no way I could top that, so I won't attempt to. #+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) + ([remap find-file] . counsel-find-file) + ("s-r" . counsel-recentf) + ("C-c x" . counsel-M-x) + ("C-c f ." . counsel-find-file) + ("C-c f 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)) #+end_src +*** eshell + +#+begin_src emacs-lisp +(use-package eshell + :commands eshell + :config + (eval-when-compile (defvar eshell-prompt-regexp)) + (defun amin/eshell-quit-or-delete-char (arg) + (interactive "p") + (if (and (eolp) (looking-back eshell-prompt-regexp nil)) + (eshell-life-is-too-much) + (delete-char arg))) + + (defun amin/eshell-clear () + (interactive) + (let ((inhibit-read-only t)) + (erase-buffer)) + (eshell-send-input)) + + (defun amin|eshell-setup () + (bind-keys :map eshell-mode-map + ("C-d" . amin/eshell-quit-or-delete-char) + ("C-l" . amin/eshell-clear))) + + :hook (eshell-mode . amin|eshell-setup)) +#+end_src + +*** Ibuffer + +#+begin_src emacs-lisp +(use-package ibuffer + :bind + (("C-x C-b" . ibuffer-other-window) + ("C-c b B" . ibuffer-other-window) + :map ibuffer-mode-map + ("P" . ibuffer-backward-filter-group) + ("N" . ibuffer-forward-filter-group) + ("M-p" . ibuffer-do-print) + ("M-n" . ibuffer-do-shell-command-pipe-replace)) + :config + ;; Use human readable Size column instead of original one + (define-ibuffer-column size-h + (:name "Size" :inline t) + (cond + ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0))) + ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0))) + ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0))) + (t (format "%8d" (buffer-size))))) + :custom + (ibuffer-saved-filter-groups + '(("default" + ("dired" (mode . dired-mode)) + ("org" (name . "^.*org$")) + ("web" + (or + (mode . web-mode) + (mode . js2-mode))) + ("shell" + (or + (mode . eshell-mode) + (mode . shell-mode))) + ("notmuch" (name . "\*notmuch\*")) + ("programming" + (or + (mode . python-mode) + (mode . c++-mode) + (mode . emacs-lisp-mode))) + ("emacs" + (or + (name . "^\\*scratch\\*$") + (name . "^\\*Messages\\*$")))))) + (ibuffer-formats + '((mark modified read-only locked " " + (name 18 18 :left :elide) + " " + (size-h 9 -1 :right) + " " + (mode 16 16 :left :elide) + " " filename-and-process) + (mark " " + (name 16 -1) + " " filename))) + :hook (ibuffer . (lambda () (ibuffer-switch-to-saved-filter-groups "default")))) +#+end_src + * Borg's =layer/essentials= TODO: break this giant source block down into individual org sections. @@ -814,9 +986,12 @@ TODO: break this giant source block down into individual org sections. (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 @@ -828,7 +1003,7 @@ TODO: break this giant source block down into individual org sections. :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) @@ -838,17 +1013,6 @@ TODO: break this giant source block down into individual org sections. (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 @@ -879,9 +1043,10 @@ TODO: break this giant source block down into individual org sections. ** [[https://leanprover.github.io][Lean]] (with [[https://github.com/leanprover/lean-mode][lean-mode]]) #+begin_src emacs-lisp +(eval-when-compile (defvar lean-mode-map)) (use-package lean-mode :bind (:map lean-mode-map - ("S-SPC" . company-complete))) + ("S-SPC" . company-complete))) #+end_src ** Haskell @@ -893,8 +1058,8 @@ TODO: break this giant source block down into individual org sections. :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]] @@ -914,8 +1079,8 @@ executable from [[https://github.com/mpickering/apply-refact][apply-refact]]. #+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 @@ -1016,8 +1181,8 @@ instead. (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 @@ -1069,6 +1234,48 @@ instead. :bind (:map haskell-mode-map ("C-c l l" . hs-lint))) #+end_src + +** Web dev + +*** SGML and HTML + +#+begin_src emacs-lisp +(use-package sgml-mode + :config + (setq sgml-basic-offset 2)) +#+end_src + +*** CSS and SCSS + +#+begin_src emacs-lisp +(use-package css-mode + :config + (setq css-indent-offset 2)) +#+end_src + +*** Web mode + +#+begin_src emacs-lisp +(use-package web-mode + :mode "\\.html\\'" + :config + (setq-every! 2 + web-mode-code-indent-offset + web-mode-css-indent-offset + web-mode-markup-indent-offset)) +#+end_src + +*** Emmet mode + +#+begin_src emacs-lisp +(use-package emmet-mode + :bind* (("C-)" . emmet-next-edit-point) + ("C-(" . emmet-prev-edit-point)) + :init + (setq emmet-move-cursor-between-quotes t) + :hook (web-mode css-mode html-mode sgml-mode)) +#+end_src + * Emacs Enhancements ** [[https://github.com/justbur/emacs-which-key][which-key]] @@ -1094,42 +1301,163 @@ Emacs package that displays available keybindings in popup ** [[https://github.com/11111000000/tao-theme-emacs][tao-theme]] -#+begin_src emacs-lisp +#+begin_src emacs-lisp :tangle no (use-package tao-theme :demand t :config (load-theme 'tao-yang t)) #+end_src -* Email -** [[https://notmuchmail.org][notmuch]] +** [[https://github.com/maio/eink-emacs][eink-theme]] #+begin_src emacs-lisp -(defun ab/notmuch () - "Delete other windows, then launch `notmuch'." - (interactive) - (require 'notmuch) - (delete-other-windows) - (notmuch)) +(load-theme 'eink t) +#+end_src + +** [[https://github.com/bbatsov/crux][crux]] + +#+begin_src emacs-lisp +(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) + ("C-c b K" . crux-kill-other-buffers) + ("C-c f c" . crux-copy-file-preserve-attributes) + ("C-c f D" . crux-delete-file-and-buffer) + ("C-c f R" . crux-rename-file-and-buffer) + ("C-S-j" . crux-top-join-line) + ("C-c j" . crux-top-join-line))) +#+end_src + +** [[https://github.com/alezost/mwim.el][mwim]] + +#+begin_src emacs-lisp +(use-package mwim + :bind (("C-a" . mwim-beginning-of-code-or-line) + ("C-e" . mwim-end-of-code-or-line) + ("" . mwim-beginning-of-line-or-code) + ("" . mwim-end-of-line-or-code))) +#+end_src + +** projectile + +#+begin_src emacs-lisp +(use-package projectile + :defer 5 + :bind-keymap ("C-c p" . projectile-command-map) + :config + (projectile-mode) + + (defun my-projectile-invalidate-cache (&rest _args) + ;; ignore the args to `magit-checkout' + (projectile-invalidate-cache nil)) + + (eval-after-load 'magit-branch + '(progn + (advice-add 'magit-checkout + :after #'my-projectile-invalidate-cache) + (advice-add 'magit-branch-and-checkout + :after #'my-projectile-invalidate-cache)))) +#+end_src + +** [[https://github.com/Wilfred/helpful][helpful]] + +#+begin_src emacs-lisp +(use-package helpful + :bind + (("C-h f" . helpful-callable) + ("C-h v" . helpful-variable) + ("C-h k" . helpful-key) + ("C-c C-d" . helpful-at-point) + ("C-h F" . helpful-function) + ("C-h C" . helpful-command))) +#+end_src -;; (ab--leader-keys -;; "m" 'ab/notmuch -;; "s" 'save-buffer -;; "SPC" 'counsel-M-x) +** [[https://github.com/kyagi/shell-pop-el][shell-pop]] -;; (map! -;; :leader -;; :desc "notmuch" :n "m" #'ab/notmuch -;; (:desc "search" :prefix "/" -;; :desc "notmuch" :n "m" #'counsel-notmuch)) +#+begin_src emacs-lisp +(use-package shell-pop + :custom + (shell-pop-universal-key "C-c e") + (shell-pop-shell-type '("eshell" "*eshell*" (lambda nil (eshell))))) #+end_src +** [[https://github.com/EricCrosson/unkillable-scratch][unkillable-scratch]] + +Make =*scratch*= and =*Messages*= unkillable. + #+begin_src emacs-lisp -(defvar ab-maildir "~/mail") +(use-package unkillable-scratch + :config + (unkillable-scratch 1) + :custom + (unkillable-buffers '("^\\*scratch\\*$" "^\\*Messages\\*$"))) +#+end_src + +** [[https://github.com/davep/boxquote.el][boxquote.el]] + +#+begin_example +,---- +| make pretty boxed quotes like this +`---- +#+end_example + +#+begin_src emacs-lisp +(use-package boxquote + :bind + (:prefix-map amin--boxquote-prefix-map + :prefix "C-c q" + ("b" . boxquote-buffer) + ("B" . boxquote-insert-buffer) + ("d" . boxquote-defun) + ("F" . boxquote-insert-file) + ("hf" . boxquote-describe-function) + ("hk" . boxquote-describe-key) + ("hv" . boxquote-describe-variable) + ("hw" . boxquote-where-is) + ("k" . boxquote-kill) + ("p" . boxquote-paragraph) + ("q" . boxquote-boxquote) + ("r" . boxquote-region) + ("s" . boxquote-shell-command) + ("t" . boxquote-text) + ("T" . boxquote-title) + ("u" . boxquote-unbox) + ("U" . boxquote-unbox-region) + ("y" . boxquote-yank) + ("M-q" . boxquote-fill-paragraph) + ("M-w" . boxquote-kill-ring-save))) +#+end_src + +Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]]. + +** [[https://github.com/DarthFennec/highlight-indent-guides][highlight-indent-guides]] + +#+begin_src emacs-lisp +(use-package highlight-indent-guides + :demand t + :hook ((prog-mode . highlight-indent-guides-mode) + (org-mode . highlight-indent-guides-mode)) + :config + (setq highlight-indent-guides-character ?\|) + (setq highlight-indent-guides-auto-enabled nil) + (setq highlight-indent-guides-method 'character) + (setq highlight-indent-guides-responsive 'top) + (set-face-foreground 'highlight-indent-guides-character-face "gainsboro") + (set-face-foreground 'highlight-indent-guides-top-character-face "grey40")) ; grey13 is nice too +#+end_src + +* Email +** [[https://notmuchmail.org][notmuch]] + +See [[notmuch:id:87muuqsvci.fsf@fencepost.gnu.org][bug follow-up]]. + +#+begin_src emacs-lisp +(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)) @@ -1140,7 +1468,7 @@ Emacs package that displays available keybindings in popup 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))) @@ -1148,12 +1476,12 @@ Emacs package that displays available keybindings in popup #'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 'notmuch-message-mode-hook + ;; (lambda () (progn + ;; (newline) + ;; (newline) + ;; (forward-line -1) + ;; (forward-line -1)))) ;; (add-hook 'message-setup-hook ;; #'mml-secure-message-sign-pgpmime) ) @@ -1162,8 +1490,16 @@ Emacs package that displays available keybindings in popup (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) + :commands notmuch + :bind ("C-c m" . amin/notmuch) + :custom (notmuch-always-prompt-for-sender t) :config (setq notmuch-hello-sections '(notmuch-hello-insert-header @@ -1172,19 +1508,31 @@ Emacs package that displays available keybindings in popup notmuch-hello-insert-alltags) notmuch-search-oldest-first nil notmuch-show-all-tags-list t + notmuch-message-headers ; see bug follow-up above + '("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) @@ -1194,65 +1542,134 @@ Emacs package that displays available keybindings in popup ;; :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))))) + ("u" . (lambda () + "Search for `unread'-tagged messages" + (interactive) + (notmuch-hello-search "tag:unread"))) + ("i" . (lambda () + "Search for `inbox'-tagged messages" + (interactive) + (notmuch-hello-search "tag:inbox"))) + ("l" . (lambda () + "Search for `latest'-tagged messages" + (interactive) + (notmuch-hello-search "tag:latest"))) + ("e" . (lambda () + "Search for `encrypted'-tagged messages" + (interactive) + (notmuch-hello-search "tag:encrypted")))) (: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) + ("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-next-thread))) + ("S" . (lambda () + "Mark message as spam" + (interactive) + (notmuch-search-tag '("-unread" "-inbox" "-webmasters" "+spam")) + (notmuch-search-next-thread)))) + (:map notmuch-tree-mode-map + ("k" . (lambda () + "Mark message read" + (interactive) + (notmuch-tree-tag '("-unread")) + ;; (notmuch-tree-archive-thread) + (notmuch-tree-next-message))) + ("u" . (lambda () + "Mark message unread" + (interactive) + (notmuch-tree-tag '("+unread")) + (notmuch-tree-next-message))) + ("K" . (lambda () + "Mark message deleted" + (interactive) + (notmuch-tree-tag '("-unread" "-inbox" "+deleted")) + (notmuch-tree-next-message))) + ("S" . (lambda () + "Mark message as spam" + (interactive) + (notmuch-tree-tag '("-unread" "-inbox" "-webmasters" "+spam")) + (notmuch-tree-next-message))))) + +(use-package counsel-notmuch + :bind ("C-c s m" . 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))) + (add-to-list 'recentf-exclude (expand-file-name amin-maildir))) +#+end_src + +** supercite + +#+begin_src emacs-lisp :tangle no +(use-package supercite + :commands sc-cite-original + :init + (add-hook 'mail-citation-hook 'sc-cite-original) + + (defun sc-remove-existing-signature () + (save-excursion + (goto-char (region-beginning)) + (when (re-search-forward message-signature-separator (region-end) t) + (delete-region (match-beginning 0) (region-end))))) + + (add-hook 'mail-citation-hook 'sc-remove-existing-signature) + + (defun sc-remove-if-not-mailing-list () + (unless (assoc "list-id" sc-mail-info) + (setq attribution sc-default-attribution + citation (concat sc-citation-delimiter + sc-citation-separator)))) + + (add-hook 'sc-attribs-postselect-hook 'sc-remove-if-not-mailing-list) + + :config + (defun sc-fill-if-different (&optional prefix) + "Fill the region bounded by `sc-fill-begin' and point. +Only fill if optional PREFIX is different than +`sc-fill-line-prefix'. If `sc-auto-fill-region-p' is nil, do not +fill region. If PREFIX is not supplied, initialize fill +variables. This is useful for a regi `begin' frame-entry." + (if (not prefix) + (setq sc-fill-line-prefix "" + sc-fill-begin (line-beginning-position)) + (if (and sc-auto-fill-region-p + (not (string= prefix sc-fill-line-prefix))) + (let ((fill-prefix sc-fill-line-prefix)) + (unless (or (string= fill-prefix "") + (save-excursion + (goto-char sc-fill-begin) + (or (looking-at ">+ +") + (< (length + (buffer-substring (point) + (line-end-position))) + 65)))) + (fill-region sc-fill-begin (line-beginning-position))) + (setq sc-fill-line-prefix prefix + sc-fill-begin (line-beginning-position))))) +nil)) +#+end_src + +* Blogging +** [[https://ox-hugo.scripter.co][ox-hugo]] + +#+begin_src emacs-lisp +(use-package ox-hugo + :after ox) #+end_src * Post initialization @@ -1265,7 +1682,7 @@ Display how long it took to load the init file. #+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 @@ -1276,3 +1693,8 @@ Display how long it took to load the init file. #+begin_src emacs-lisp :comments none ;;; init.el ends here #+end_src + +* COMMENT Local Variables :ARCHIVE: +# Local Variables: +# eval: (add-hook 'after-save-hook #'amin/async-babel-tangle 'append 'local) +# End: