[emacs] update 5 drones
[~bandali/configs] / init.org
index cb5d1f0..5dacc59 100644 (file)
--- a/init.org
+++ b/init.org
@@ -234,6 +234,9 @@ can then be managed with the help of Magit or other tools.
 (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
@@ -574,7 +577,8 @@ variable.
 
 #+begin_src emacs-lisp
 (setq backup-by-copying t
-      version-control t)
+      version-control t
+      delete-old-versions t)
 #+end_src
 
 *** Auto revert
@@ -604,6 +608,20 @@ Enable =winner-mode=.
 (winner-mode 1)
 #+end_src
 
+*** Close =*compilation*= on success
+
+#+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
+
 ** Bindings
 
 #+begin_src emacs-lisp
@@ -662,6 +680,54 @@ In short, my favourite way of life.
   :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]]
 
 #+begin_quote
@@ -750,6 +816,33 @@ There's no way I could top that, so I won't attempt to.
   (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
+
 * Borg's =layer/essentials=
 
 TODO: break this giant source block down into individual org sections.
@@ -883,6 +976,7 @@ 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)))
@@ -1119,7 +1213,9 @@ Emacs package that displays available keybindings in popup
          ("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-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]]
@@ -1139,7 +1235,7 @@ Emacs package that displays available keybindings in popup
   :defer 5
   :bind-keymap ("C-c p" . projectile-command-map)
   :config
-  (projectile-global-mode)
+  (projectile-mode)
 
   (defun my-projectile-invalidate-cache (&rest _args)
     ;; ignore the args to `magit-checkout'
@@ -1153,6 +1249,98 @@ Emacs package that displays available keybindings in popup
                    :after #'my-projectile-invalidate-cache))))
 #+end_src
 
+** [[https://github.com/wasamasa/shackle][shackle]]
+
+#+begin_src emacs-lisp
+(use-package shackle
+  :demand t
+  :commands shackle-mode
+  :config
+  (shackle-mode 1)
+  (setq shackle-rules
+        '(("*Help*" :align right :select t :size 0.5)
+          ("\\`\\*helm.*?\\*\\'" :regexp t :align t)
+          ((compilation-mode "\\`\\*magit-diff: .*?\\'") :regexp t :noselect t)
+          ("*magit-dispatch-popup*" :align below)
+          ((inferior-scheme-mode "*shell*" "*eshell*") :popup t))
+        shackle-default-rule '(:select t)
+        shackle-default-size 0.4
+        shackle-inhibit-window-quit-on-same-windows t))
+#+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
+
+** [[https://github.com/kyagi/shell-pop-el][shell-pop]]
+
+#+begin_src emacs-lisp
+(use-package shell-pop
+  :config
+  (add-to-list 'shackle-rules '("\\*eshell\\*" :regexp t :same t))
+  :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
+(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]].
+
 * Email
 ** [[https://notmuchmail.org][notmuch]]
 
@@ -1207,6 +1395,7 @@ See [[notmuch:id:87muuqsvci.fsf@fencepost.gnu.org][bug follow-up]].
 (use-package 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
@@ -1247,13 +1436,24 @@ See [[notmuch:id:87muuqsvci.fsf@fencepost.gnu.org][bug follow-up]].
   ;; (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
+        ("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
         ("k" . (lambda ()
                  "Mark message read"
@@ -1270,19 +1470,34 @@ See [[notmuch:id:87muuqsvci.fsf@fencepost.gnu.org][bug follow-up]].
                  "Mark message deleted"
                  (interactive)
                  (notmuch-search-tag '("-unread" "-inbox" "+deleted"))
-                 (notmuch-search-archive-thread)))
+                 (notmuch-search-next-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
+                 (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-archive-thread))))
-)
+                 (notmuch-tree-next-message)))))
 
 (use-package counsel-notmuch
   :bind ("C-c s m" . counsel-notmuch))
@@ -1374,3 +1589,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: