(require 'borg)
(borg-initialize)
+(require 'borg-nix-shell)
+(setq borg-build-shell-command 'borg-nix-shell-build-command)
+
(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
- ("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=
(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=
`(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
:PROPERTIES:
:CUSTOM_ID: core
#+begin_src emacs-lisp
(setq backup-by-copying t
- version-control t)
+ version-control t
+ delete-old-versions t)
#+end_src
*** Auto revert
(winner-mode 1)
#+end_src
-** Bindings
-
-#+begin_src emacs-lisp :tangle no
-(bind-keys
- ; buffers
- ("M-m b b" . ibuffer-list-buffers)
- ("M-m b k" . kill-this-buffer)
- ("M-m b s" . save-buffer)
+*** Close =*compilation*= on success
- ; 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)
+#+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
- ("M-m o" . other-window)
- ("M-m w o" . other-window)
+** Bindings
- ("M-m q q" . save-buffers-kill-terminal))
+#+begin_src emacs-lisp
+(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
: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
(("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)
("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)
(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)
: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
(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.
** [[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)))
: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]]
(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>" . 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
+
+** [[https://github.com/kyagi/shell-pop-el][shell-pop]]
+
+#+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
+(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]]
(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
;; (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
- :bind ("M-m / m" . counsel-notmuch))
+ :bind ("C-c s m" . counsel-notmuch))
(after! notmuch-crypto
(setq notmuch-crypto-process-mime t))
#+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: