[emacs] automatically tangle init.org on save
[~bandali/configs] / init.org
index b6d853c..81c652e 100644 (file)
--- a/init.org
+++ b/init.org
@@ -234,17 +234,16 @@ can then be managed with the help of Magit or other tools.
 (require 'borg)
 (borg-initialize)
 
 (require 'borg)
 (borg-initialize)
 
+(require 'borg-nix-shell)
+(setq borg-build-shell-command 'borg-nix-shell-build-command)
+
 (with-eval-after-load 'bind-key
 (with-eval-after-load 'bind-key
-  ; unbind M-m for use as a personal prefix
-  (unbind-key "M-m"     global-map)
-  (bind-key   "M-m M-m" 'back-to-indentation)
-  ; add some bindings for Borg
   (bind-keys
    :package borg
   (bind-keys
    :package borg
-   ("M-m B A" . borg-activate)
-   ("M-m B a" . borg-assimilate)
-   ("M-m B b" . borg-build)
-   ("M-m B c" . borg-clone)))
+   ("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=
 #+end_src
 
 *** =use-package=
@@ -282,10 +281,10 @@ database, low-level functions for querying the database, and a
 (use-package epkg
   :defer t
   :bind
 (use-package epkg
   :defer t
   :bind
-  (("M-m B d" . epkg-describe-package)
-   ("M-m B p" . epkg-list-packages)
-   ("M-m B r" . borg-remove)
-   ("M-m B u" . epkg-update)))
+  (("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=
 #+end_src
 
 ** No littering in =~/.emacs.d=
@@ -610,27 +609,14 @@ Enable =winner-mode=.
 
 ** Bindings
 
 
 ** Bindings
 
-#+begin_src emacs-lisp :tangle no
+#+begin_src emacs-lisp
 (bind-keys
 (bind-keys
- ; buffers
- ("M-m b b" . ibuffer-list-buffers)
- ("M-m b k" . kill-this-buffer)
- ("M-m b s" . save-buffer)
-
- ; help
- ("M-m h c" . describe-char)
- ("M-m h f" . describe-function)
- ("M-m h F" . describe-face)
- ("M-m h i" . info)
- ("M-m h k" . describe-key)
- ("M-m h l" . view-lossage)
- ("M-m h m" . describe-mode)
- ("M-m h v" . describe-variable)
-
- ("M-m o"   . other-window)
- ("M-m w o" . other-window)
-
- ("M-m q q" . save-buffers-kill-terminal))
+ ("C-c b B" . ibuffer-list-buffers)
+ ("C-c b k" . kill-this-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
 #+end_src
 
 ** Packages
@@ -679,6 +665,48 @@ In short, my favourite way of life.
   :after (:any org notmuch))
 #+end_src
 
   :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.")
+
+  (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
+                (message "Tangled %s%s"
+                         ,file-nodir
+                         (if amin-show-async-tangle-time
+                             (format " (%.3fs)"
+                                     (float-time (time-subtract (current-time)
+                                                                ',file-tangle-start-time)))
+                           ""))
+              (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
 *** [[https://magit.vc/][Magit]]
 
 #+begin_quote
@@ -694,7 +722,7 @@ Not just how I do git, but /the/ way to do git.
   (("s-g"   . magit-dispatch-popup)
    ("C-x g" . magit-status)
    :prefix-map amin--magit-prefix-map
   (("s-g"   . magit-dispatch-popup)
    ("C-x g" . magit-status)
    :prefix-map amin--magit-prefix-map
-   :prefix     "M-m g"
+   :prefix "C-c g"
    ("SPC" . magit-status)
    ("s"   . magit-status)
    ("S"   . magit-status-prefix)
    ("SPC" . magit-status)
    ("s"   . magit-status)
    ("S"   . magit-status-prefix)
@@ -703,6 +731,7 @@ Not just how I do git, but /the/ way to do git.
    ("f"   . magit-fetch)
    ("F"   . magit-pull)
    ("P"   . magit-push)
    ("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)
    ("c c" . magit-commit)
    ("c a" . magit-commit-amend)
    ("b b" . magit-checkout)
@@ -729,7 +758,7 @@ There's no way I could top that, so I won't attempt to.
 (use-package ivy
   :defer 1
   :bind
 (use-package ivy
   :defer 1
   :bind
-  (("M-m ," . ivy-switch-buffer)
+  (("C-c b b" . ivy-switch-buffer)
    :map ivy-minibuffer-map
    ([escape] . keyboard-escape-quit)
    ([S-up]   . ivy-previous-history-element)
    :map ivy-minibuffer-map
    ([escape] . keyboard-escape-quit)
    ([S-up]   . ivy-previous-history-element)
@@ -756,9 +785,9 @@ There's no way I could top that, so I won't attempt to.
   :bind (([remap execute-extended-command] . counsel-M-x)
          ([remap find-file]                . counsel-find-file)
          ("s-r"                            . counsel-recentf)
   :bind (([remap execute-extended-command] . counsel-M-x)
          ([remap find-file]                . counsel-find-file)
          ("s-r"                            . counsel-recentf)
-         ("M-m SPC"                        . counsel-M-x)
-         ("M-m ."                          . counsel-find-file)
-         ("M-m f 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))
   :config
          :map minibuffer-local-map
          ("C-r" . counsel-minibuffer-history))
   :config
@@ -766,6 +795,33 @@ There's no way I could top that, so I won't attempt to.
   (defalias 'locate #'counsel-locate))
 #+end_src
 
   (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.
 * Borg's =layer/essentials=
 
 TODO: break this giant source block down into individual org sections.
@@ -899,6 +955,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
 ** [[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)))
 (use-package lean-mode
   :bind (:map lean-mode-map
               ("S-SPC" . company-complete)))
@@ -1132,10 +1189,12 @@ Emacs package that displays available keybindings in popup
 (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)
 (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)
-         ("M-m b K" . crux-kill-other-buffers)
-         ("M-m f c" . crux-copy-file-preserve-attributes)
-         ("M-m f D" . crux-delete-file-and-buffer)
-         ("M-m f R" . crux-rename-file-and-buffer)))
+         ("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]]
 #+end_src
 
 ** [[https://github.com/alezost/mwim.el][mwim]]
@@ -1148,6 +1207,70 @@ Emacs package that displays available keybindings in popup
          ("<end>"  . mwim-end-of-line-or-code)))
 #+end_src
 
          ("<end>"  . 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/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 bottom)
+          ((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
+
 * Email
 ** [[https://notmuchmail.org][notmuch]]
 
 * Email
 ** [[https://notmuchmail.org][notmuch]]
 
@@ -1201,8 +1324,8 @@ See [[notmuch:id:87muuqsvci.fsf@fencepost.gnu.org][bug follow-up]].
 
 (use-package notmuch
   :commands notmuch
 
 (use-package notmuch
   :commands notmuch
-  :bind (("C-c m" . amin/notmuch)
-         ("M-m m" . amin/notmuch))
+  :bind ("C-c m" . amin/notmuch)
+  :custom (notmuch-always-prompt-for-sender t)
   :config
   (setq notmuch-hello-sections
         '(notmuch-hello-insert-header
   :config
   (setq notmuch-hello-sections
         '(notmuch-hello-insert-header
@@ -1243,13 +1366,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)
   ;; (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
   :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"
   (:map notmuch-search-mode-map
         ("k" . (lambda ()
                  "Mark message read"
@@ -1266,22 +1400,37 @@ See [[notmuch:id:87muuqsvci.fsf@fencepost.gnu.org][bug follow-up]].
                  "Mark message deleted"
                  (interactive)
                  (notmuch-search-tag '("-unread" "-inbox" "+deleted"))
                  "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"))
         ("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"))
         ("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
 
 (use-package counsel-notmuch
-  :bind ("M-m / m" . counsel-notmuch))
+  :bind ("C-c s m" . counsel-notmuch))
 
 (after! notmuch-crypto
   (setq notmuch-crypto-process-mime t))
 
 (after! notmuch-crypto
   (setq notmuch-crypto-process-mime t))
@@ -1370,3 +1519,8 @@ Display how long it took to load the init file.
 #+begin_src emacs-lisp :comments none
 ;;; init.el ends here
 #+end_src
 #+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: