X-Git-Url: https://git.shemshak.org/~bandali/configs/blobdiff_plain/a219024e13f754887cbbe1481823765bc2802263..0884b63b028514ff55208af9020e0dce5700e802:/init.org diff --git a/init.org b/init.org index 045845d..2bd523e 100644 --- a/init.org +++ b/init.org @@ -104,28 +104,12 @@ file. #+begin_src emacs-lisp :comments none ;;; Commentary: -;; Emacs configuration of Amin Bandali, computer scientist and functional -;; programmer. +;; Emacs configuration of Amin Bandali, computer scientist, functional +;; programmer, and free software advocate. ;; THIS FILE IS AUTO-GENERATED FROM `init.org'. #+end_src -** Naming conventions - -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: -;; -;; 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|... a hook function -;; amin*... an advising function -;; amin@... a hydra command -;; ...! a macro -#+end_src - * Initial setup :PROPERTIES: :CUSTOM_ID: initial-setup @@ -138,10 +122,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 amin--before-user-init-time (current-time) +(defvar a/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 amin--before-user-init-time + (float-time (time-subtract a/before-user-init-time before-init-time))) #+end_src @@ -151,9 +135,9 @@ frequency. Clearing the ~file-name-handler-alist~ seems to help reduce startup time as well. #+begin_src emacs-lisp -(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) +(defvar a/gc-cons-threshold gc-cons-threshold) +(defvar a/gc-cons-percentage gc-cons-percentage) +(defvar a/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 @@ -168,9 +152,9 @@ done initializing. (add-hook 'after-init-hook (lambda () - (setq gc-cons-threshold amin--gc-cons-threshold - gc-cons-percentage amin--gc-cons-percentage - file-name-handler-alist amin--file-name-handler-alist))) + (setq gc-cons-threshold a/gc-cons-threshold + gc-cons-percentage a/gc-cons-percentage + file-name-handler-alist a/file-name-handler-alist))) #+end_src Increase the number of lines kept in message logs (the =*Messages*= @@ -286,7 +270,12 @@ database, low-level functions for querying the database, and a :bind (("C-c b d" . epkg-describe-package) ("C-c b p" . epkg-list-packages) - ("C-c b u" . epkg-update))) + ("C-c b u" . epkg-update)) + :config + (eval-when-compile (defvar ivy-initial-inputs-alist)) + (with-eval-after-load 'ivy + (add-to-list + 'ivy-initial-inputs-alist '(epkg-describe-package . "^") t))) #+end_src ** No littering in =~/.emacs.d= @@ -433,7 +422,7 @@ Let’s define a function to conveniently disable this for certain buffers and/or modes. #+begin_src emacs-lisp -(defun amin--no-mouse-autoselect-window () +(defun a/no-mouse-autoselect-window () (make-local-variable 'mouse-autoselect-window) (setq mouse-autoselect-window nil)) #+end_src @@ -447,48 +436,35 @@ buffers and/or modes. ** Useful utilities -#+begin_src emacs-lisp -(defun amin-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 (amin-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 - Convenience macro for =setq='ing multiple variables to the same value: #+begin_src emacs-lisp -(defmacro setq-every! (value &rest vars) +(defmacro a/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 +The following process-related stuff from [[https://github.com/alezost/emacs-config][alezost's emacs-config]]. + +#+begin_src emacs-lisp +(defun a/start-process (program &rest args) + "Same as `start-process', but doesn't bother about name and buffer." + (let ((process-name (concat program "_process")) + (buffer-name (generate-new-buffer-name + (concat program "_output")))) + (apply #'start-process + process-name buffer-name program args))) + +(defun a/dired-start-process (program &optional args) + "Open current file with a PROGRAM." + ;; Shell command looks like this: "program [ARGS]... FILE" (ARGS can + ;; be nil, so remove it). + (apply #'a/start-process + program + (remove nil (list args (dired-get-file-for-visit))))) +#+end_src + * Core :PROPERTIES: :CUSTOM_ID: core @@ -659,14 +635,13 @@ Based on https://stackoverflow.com/a/17788551, with changes to use =cl-letf= instead of the now obsolete =flet=. #+begin_src emacs-lisp -(use-package compile - :config - (defun amin--compilation-finish-function (buffer outstr) +(with-eval-after-load 'compile + (defun a/compilation-finish-function (buffer outstr) (unless (string-match "finished" outstr) (switch-to-buffer-other-window buffer)) t) - (setq compilation-finish-functions #'amin--compilation-finish-function) + (setq compilation-finish-functions #'a/compilation-finish-function) (require 'cl-macs) @@ -693,6 +668,12 @@ for this. ;; (setq replace-char-fold t) #+end_src +*** Cursor shape + +#+begin_src emacs-lisp +(setq-default cursor-type 'bar) +#+end_src + ** Bindings #+begin_src emacs-lisp @@ -762,6 +743,9 @@ In short, my favourite way of life. org-edit-src-content-indentation 0 org-email-link-description-format "Email %c: %s" ; %.30s org-highlight-latex-and-related '(entities) + org-use-speed-commands t + org-startup-folded 'content + org-catch-invisible-edits 'show-and-error org-log-done 'time) (add-to-list 'org-structure-template-alist '("L" . "src emacs-lisp") t) (font-lock-add-keywords @@ -809,22 +793,22 @@ In short, my favourite way of life. **** asynchronous tangle -=amin/async-babel-tangle= is a function closely inspired by [[https://github.com/dieggsy/dotfiles/tree/cc10edf7701958eff1cd94d4081da544d882a28c/emacs.d#dotfiles][dieggsy's +=a/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 +(with-eval-after-load 'org + (defvar a/show-async-tangle-results nil "Keep *emacs* async buffers around for later inspection.") - (defvar amin-show-async-tangle-time nil + (defvar a/show-async-tangle-time nil "Show the time spent tangling the file.") - (defvar amin-async-tangle-post-compile "make ti" + (defvar a/async-tangle-post-compile "make ti" "If non-nil, pass to `compile' after successful tangle.") - (defun amin/async-babel-tangle () + (defun a/async-babel-tangle () "Tangle org file asynchronously." (interactive) (let* ((file-tangle-start-time (current-time)) @@ -836,24 +820,24 @@ file. `(lambda () (require 'org) (org-babel-tangle-file ,file)) - (unless amin-show-async-tangle-results + (unless a/show-async-tangle-results `(lambda (result) (if result (progn (message "Tangled %s%s" ,file-nodir - (if amin-show-async-tangle-time + (if a/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))) + (when a/async-tangle-post-compile + (compile a/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)) + '(eval add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local)) #+end_src *** [[https://magit.vc/][Magit]] @@ -949,28 +933,28 @@ There's no way I could top that, so I won't attempt to. :bind ("C-c a s e" . eshell) :config (eval-when-compile (defvar eshell-prompt-regexp)) - (defun amin/eshell-quit-or-delete-char (arg) + (defun a/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 () + (defun a/eshell-clear () (interactive) (let ((inhibit-read-only t)) (erase-buffer)) (eshell-send-input)) - (defun amin|eshell-setup () + (defun a/eshell-setup () (make-local-variable 'company-idle-delay) (defvar company-idle-delay nil) (bind-keys :map eshell-mode-map - ("C-d" . amin/eshell-quit-or-delete-char) - ("C-S-l" . amin/eshell-clear) + ("C-d" . a/eshell-quit-or-delete-char) + ("C-S-l" . a/eshell-clear) ("M-r" . counsel-esh-history) ([tab] . company-complete))) - :hook (eshell-mode . amin|eshell-setup) + :hook (eshell-mode . a/eshell-setup) :custom (eshell-hist-ignoredups t) (eshell-input-filter 'eshell-input-filter-initial-space)) @@ -1059,7 +1043,7 @@ There's no way I could top that, so I won't attempt to. ("" . outline-toggle-children) ("M-p" . outline-previous-visible-heading) ("M-n" . outline-next-visible-heading) - :prefix-map amin--outline-prefix-map + :prefix-map a/outline-prefix-map :prefix "s-o" ("TAB" . outline-toggle-children) ("a" . outline-hide-body) @@ -1088,7 +1072,39 @@ TODO: break this giant source block down into individual org sections. (use-package dired :defer t - :config (setq dired-listing-switches "-alh")) + :config + (setq dired-listing-switches "-alh") + + ;; easily diff 2 marked files + ;; https://oremacs.com/2017/03/18/dired-ediff/ + (defun dired-ediff-files () + (interactive) + (require 'ediff) + (let ((files (dired-get-marked-files)) + (wnd (current-window-configuration))) + (if (<= (length files) 2) + (let ((file1 (car files)) + (file2 (if (cdr files) + (cadr files) + (read-file-name + "file: " + (dired-dwim-target-directory))))) + (if (file-newer-than-file-p file1 file2) + (ediff-files file2 file1) + (ediff-files file1 file2)) + (add-hook 'ediff-after-quit-hook-internal + (lambda () + (setq ediff-after-quit-hook-internal nil) + (set-window-configuration wnd)))) + (error "no more than 2 files should be marked")))) + :bind (:map dired-mode-map + ("e" . dired-ediff-files) + ("E" . dired-toggle-read-only) + ("\\" . dired-hide-details-mode) + ("z" . (lambda () + (interactive) + (a/dired-start-process "zathura")))) + :hook (dired-mode . dired-hide-details-mode)) (use-package eldoc :when (version< "25" emacs-version) @@ -1224,6 +1240,7 @@ TODO: break this giant source block down into individual org sections. (advice-add #'ispell-parse-output :filter-args #'endless/replace-quote)) #+end_src + * Programming modes :PROPERTIES: :CUSTOM_ID: programming-modes @@ -1479,7 +1496,7 @@ instead. :defer t :mode "\\.html\\'" :config - (setq-every! 2 + (a/setq-every 2 web-mode-code-indent-offset web-mode-css-indent-offset web-mode-markup-indent-offset)) @@ -1637,14 +1654,14 @@ Emacs package that displays available keybindings in popup ** theme helper functions #+begin_src emacs-lisp -(defun amin/lights-on () +(defun a/lights-on () "Enable my favourite light theme." (interactive) (mapc #'disable-theme custom-enabled-themes) (load-theme 'tangomod t) (sml/apply-theme 'automatic)) -(defun amin/lights-off () +(defun a/lights-off () "Go dark." (interactive) (mapc #'disable-theme custom-enabled-themes) @@ -1652,8 +1669,8 @@ Emacs package that displays available keybindings in popup (sml/apply-theme 'automatic)) (bind-keys - ("s-t d" . amin/lights-off) - ("s-t l" . amin/lights-on)) + ("s-t d" . a/lights-off) + ("s-t l" . a/lights-on)) #+end_src ** [[https://github.com/bbatsov/crux][crux]] @@ -1699,7 +1716,8 @@ Emacs package that displays available keybindings in popup (advice-add 'magit-checkout :after #'my-projectile-invalidate-cache) (advice-add 'magit-branch-and-checkout - :after #'my-projectile-invalidate-cache)))) + :after #'my-projectile-invalidate-cache))) + :custom (projectile-completion-system 'ivy)) #+end_src ** [[https://github.com/Wilfred/helpful][helpful]] @@ -1741,7 +1759,7 @@ Make =*scratch*= and =*Messages*= unkillable. (use-package boxquote :defer 3 :bind - (:prefix-map amin--boxquote-prefix-map + (:prefix-map a/boxquote-prefix-map :prefix "C-c q" ("b" . boxquote-buffer) ("B" . boxquote-insert-buffer) @@ -1767,42 +1785,6 @@ Make =*scratch*= and =*Messages*= unkillable. Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]]. -** COMMENT [[https://github.com/DarthFennec/highlight-indent-guides][highlight-indent-guides]] - -#+begin_src emacs-lisp -(use-package highlight-indent-guides - :defer 3 - :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 - -** pdf-tools - -#+begin_src emacs-lisp -(use-package pdf-tools - :defer t - :magic ("%PDF" . pdf-view-mode) - :config - (setq pdf-view-resize-factor 1.05) - (pdf-tools-install) - :bind - (:map pdf-view-mode-map - ("C-s" . isearch-forward) - ("C-r" . isearch-backward) - ("j" . pdf-view-next-line-or-next-page) - ("k" . pdf-view-previous-line-or-previous-page) - ("h" . image-backward-hscroll) - ("l" . image-forward-hscroll))) -#+end_src - ** typo.el #+begin_src emacs-lisp @@ -1853,71 +1835,6 @@ Also see [[https://www.emacswiki.org/emacs/rebox2][rebox2]]. "")))) #+end_src -** COMMENT slack - -Hopefully temporary. - -#+begin_src emacs-lisp -(use-package slack - :commands (slack-start) - :init - (eval-when-compile ; silence the byte-compiler - (defvar url-http-data nil) - (defvar url-http-extra-headers nil) - (defvar url-http-method nil) - (defvar url-callback-function nil) - (defvar url-callback-arguments nil) - (defvar oauth--token-data nil)) - (setq slack-buffer-emojify t - slack-prefer-current-team t) - :config - (slack-register-team - :name "uw-apv" - :default t - :client-id uw-apv-client-id - :client-secret uw-apv-client-secret - :token uw-apv-token - :subscribed-channels '(general) - :full-and-display-names t) - (slack-register-team - :name "watform" - :default nil - :client-id watform-client-id - :client-secret watform-client-secret - :token watform-token - :subscribed-channels '(general) - :full-and-display-names t) - (add-to-list 'swiper-font-lock-exclude 'slack-message-buffer-mode t) - (setq lui-time-stamp-format "[%Y-%m-%d %H:%M:%S]" - lui-time-stamp-only-when-changed-p t - lui-time-stamp-position 'right) - :bind - (("C-c s s" . slack-start) - ("C-c s u" . slack-select-unread-rooms) - ("C-c s b" . slack-select-rooms) - ("C-c s t" . slack-change-current-team) - ("C-c s c" . slack-ws-close) - :map slack-mode-map - ("M-p" . slack-buffer-goto-prev-message) - ("M-n" . slack-buffer-goto-next-message) - ("C-c e" . slack-message-edit) - ("C-c k" . slack-message-delete) - ("C-c C-k" . slack-channel-leave) - ("C-c r a" . slack-message-add-reaction) - ("C-c r r" . slack-message-remove-reaction) - ("C-c r s" . slack-message-show-reaction-users) - ("C-c p l" . slack-room-pins-list) - ("C-c p a" . slack-message-pins-add) - ("C-c p r" . slack-message-pins-remove) - ("@" . slack-message-embed-mention) - ("#" . slack-message-embed-channel))) - -(use-package alert - :commands (alert) - :init - (setq alert-default-style 'notifier)) -#+end_src - ** [[https://github.com/peterwvj/eshell-up][eshell-up]] #+begin_src emacs-lisp @@ -1976,24 +1893,26 @@ Hopefully temporary. :bind ("C-=" . er/expand-region)) #+end_src +** TODO multiple-cursors + * Email :PROPERTIES: :CUSTOM_ID: email :END: #+begin_src emacs-lisp -(defvar amin-maildir (expand-file-name "~/mail/")) -(after! recentf - (add-to-list 'recentf-exclude amin-maildir)) +(defvar a/maildir (expand-file-name "~/mail/")) +(with-eval-after-load 'recentf + (add-to-list 'recentf-exclude a/maildir)) #+end_src ** Gnus #+begin_src emacs-lisp (setq - amin-gnus-init-file (no-littering-expand-etc-file-name "gnus") - mail-user-agent 'gnus-user-agent - read-mail-command 'gnus) + a/gnus-init-file (no-littering-expand-etc-file-name "gnus") + mail-user-agent 'gnus-user-agent + read-mail-command 'gnus) (use-package gnus :bind (("C-c m" . gnus) @@ -2061,7 +1980,7 @@ Hopefully temporary. (use-package gnus-sum :bind (:map gnus-summary-mode-map - :prefix-map amin--gnus-summary-prefix-map + :prefix-map a/gnus-summary-prefix-map :prefix "v" ("r" . gnus-summary-reply) ("w" . gnus-summary-wide-reply) @@ -2072,7 +1991,7 @@ Hopefully temporary. ("r" . gnus-summary-reply-with-original) ("R" . gnus-summary-wide-reply-with-original) ("M-L" . org-store-link)) - :hook (gnus-summary-mode . amin--no-mouse-autoselect-window)) + :hook (gnus-summary-mode . a/no-mouse-autoselect-window)) (use-package gnus-msg :config @@ -2080,13 +1999,13 @@ Hopefully temporary. '((".*" (address "amin@aminb.org") (body "\nBest,\namin\n") - (eval (setq amin--message-cite-say-hi t))) + (eval (setq a/message-cite-say-hi t))) ("gnu.*" (address "bandali@gnu.org")) ((header "subject" "ThankCRM") (to "webmasters-comment@gnu.org") (body "\nAdded to 2018supporters.html.\n\nMoving to campaigns.\n\n-amin\n") - (eval (setq amin--message-cite-say-hi nil))) + (eval (setq a/message-cite-say-hi nil))) ("nnimap\\+uwaterloo:.*" (address "abandali@uwaterloo.ca") (gcc "\"nnimap+uwaterloo:Sent Items\""))))) @@ -2124,7 +2043,7 @@ Hopefully temporary. #+begin_src emacs-lisp (use-package message :config - (defconst amin--message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:") + (defconst a/message-cite-style-format "On %Y-%m-%d %l:%M %p, %N wrote:") (defconst message-cite-style-bandali '((message-cite-function 'message-cite-original) (message-citation-line-function 'message-insert-formatted-citation-line) @@ -2133,9 +2052,9 @@ Hopefully temporary. (message-yank-cited-prefix ">") (message-yank-empty-prefix ">") (message-citation-line-format - (if amin--message-cite-say-hi - (concat "Hi %F,\n\n" amin--message-cite-style-format) - amin--message-cite-style-format))) + (if a/message-cite-say-hi + (concat "Hi %F,\n\n" a/message-cite-style-format) + a/message-cite-style-format))) "Citation style based on Mozilla Thunderbird's. Use with message-cite-style.") (setq message-cite-style 'message-cite-style-bandali message-kill-buffer-on-exit t @@ -2157,7 +2076,7 @@ Hopefully temporary. ;; (message-header-cc ((t (:foreground "#333" :weight normal)))) ) -(after! mml-sec +(with-eval-after-load 'mml-sec (setq mml-secure-openpgp-encrypt-to-self t mml-secure-openpgp-sign-with-sender t)) #+end_src @@ -2171,7 +2090,7 @@ Convenient footnotes in =message-mode=. :after message :bind (:map message-mode-map - :prefix-map amin--footnote-prefix-map + :prefix-map a/footnote-prefix-map :prefix "C-c f" ("a" . footnote-add-footnote) ("b" . footnote-back-to-message) @@ -2194,7 +2113,7 @@ Convenient footnotes in =message-mode=. :bind (:map gnus-group-mode-map ("e" . ebdb)) :config (setq ebdb-sources (no-littering-expand-var-file-name "ebdb")) - (after! swiper + (with-eval-after-load 'swiper (add-to-list 'swiper-font-lock-exclude 'ebdb-mode t))) (use-package ebdb-com @@ -2238,39 +2157,6 @@ Convenient footnotes in =message-mode=. ;; :after ebdb) #+end_src -** COMMENT bbdb - -Manually install bbdb (=lisp/bbdb= copied from an ELPA-based setup), -because installing it from source on Emacs 27 using the following -submodule configuration for some reason doesn’t work and results in -very strange errors when using any of the functions. - -#+begin_src conf :tangle no -[submodule "bbdb"] - path = lib/bbdb - url = https://git.savannah.nongnu.org/git/bbdb.git - load-path = lisp - info-path = doc - build-step = ./autogen.sh - build-step = ./configure - build-step = make - build-step = make install -#+end_src - -I tried using =borg-elpa= instead of doing it like this, but it added -2 seconds to my startup time, which is unacceptable to me. - -#+begin_src emacs-lisp -(use-package bbdb - :load-path "lisp/bbdb" - :init - (load (expand-file-name "lisp/bbdb/bbdb-autoloads.el" user-emacs-directory)) - ;; (bbdb-mua-auto-update-init 'message) - (setq bbdb-mua-auto-update-p 'query - bbdb-complete-mail nil) - (bbdb-initialize 'gnus 'message)) -#+end_src - ** COMMENT message-x #+begin_src emacs-lisp @@ -2323,7 +2209,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) - amin--before-user-init-time))) + a/before-user-init-time))) #+end_src * Footer @@ -2337,6 +2223,6 @@ Display how long it took to load the init file. * COMMENT Local Variables :ARCHIVE: # Local Variables: -# eval: (add-hook 'after-save-hook #'amin/async-babel-tangle 'append 'local) +# eval: (add-hook 'after-save-hook #'a/async-babel-tangle 'append 'local) # eval: (typo-mode -1) # End: