(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=
#+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=
#+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
tab-width 4)
#+end_src
+*** Winner mode
+
+Enable =winner-mode=.
+
+#+begin_src emacs-lisp
+(winner-mode 1)
+#+end_src
+
+** Bindings
+
+#+begin_src emacs-lisp
+(bind-keys
+ ("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
The packages in this section are absolutely essential to my everyday
'auto-compile-inhibit-compile-detached-git-head))
#+end_src
-*** [[https://github.com/noctuid/general.el][general]]
-
- #+begin_src emacs-lisp
-(use-package general
- :demand t
- :config
- (general-evil-setup t)
-
- (general-override-mode)
-
- (general-create-definer
- amin--mode-leader-keys
- :keymaps 'override
- :states '(emacs normal visual motion insert)
- :non-normal-prefix "C-,"
- :prefix ",")
-
- (general-create-definer
- amin--leader-keys
- :keymaps 'override
- :states '(emacs normal visual motion insert)
- :non-normal-prefix "M-m"
- :prefix "SPC"))
-#+end_src
-
-*** evil
-
-#+begin_src emacs-lisp
-(use-package evil
- :demand t
- :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
-(use-package evil-escape
- :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))
-#+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
#+begin_src emacs-lisp
(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
: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))
+
+(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
+
*** [[https://magit.vc/][Magit]]
#+begin_quote
#+begin_src emacs-lisp
(use-package magit
- :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 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
(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))
- :general (amin--leader-keys "," 'ivy-switch-buffer)
+ (("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))
#+begin_src emacs-lisp
(use-package swiper
- :general (:states '(normal motion) "/" 'swiper)
- (amin--leader-keys "//" 'swiper)
:bind (([remap isearch-forward] . swiper)
([remap isearch-backward] . swiper)))
#+end_src
#+begin_src emacs-lisp
(use-package counsel
:defer 1
- :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)
+ ([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))
:config
(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.
** [[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)))
#+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))
- :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))
+ :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]]
: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>" . 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
(notmuch))
(use-package notmuch
- :general (amin--leader-keys "m" 'amin/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
;; (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"
"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
- :general (amin--leader-keys "/m" '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 amin-maildir)))
#+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: