656 lines
20 KiB
Org Mode
656 lines
20 KiB
Org Mode
#+TITLE: Emacs Configuration
|
|
#+AUTHOR: Camden Dixie O'Brien
|
|
#+ATTR_LATEX: :float t
|
|
|
|
Shout out to Harry R. Schwartz; A whole bunch of this config
|
|
(including the idea of embeddeding the lot in an Org document) is
|
|
yanked from [[https://github.com/hrs/dotfiles][his dotfiles repo]].
|
|
|
|
The rest of this config grabs packages via =use-package=, so that
|
|
needs to be set up to install them if they aren't already.
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'use-package-ensure)
|
|
(setq use-package-always-ensure t)
|
|
#+end_src
|
|
|
|
* UI
|
|
The start-up message gets pretty annoying, so disable that.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq inhibit-startup-screen t)
|
|
#+end_src
|
|
|
|
I like a little more line spacing than default.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default line-spacing 0.2)
|
|
#+end_src
|
|
|
|
Also, the menu-, tool- and scroll-bar are ugly, take up space and I
|
|
don't use them.
|
|
|
|
#+begin_src emacs-lisp
|
|
(menu-bar-mode -1)
|
|
(tool-bar-mode -1)
|
|
(scroll-bar-mode -1)
|
|
#+end_src
|
|
|
|
It's nice to have an inverse of =C-x o= for switching between
|
|
windows. =other-window=, the function that =C-x o= is bound to,
|
|
takes an argument =COUNT= that determines how many windows it skips
|
|
forwards so we can simply pass -1 to =other-window= in a lambda and
|
|
bind to that:
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-set-key (kbd "C-x O")
|
|
(lambda ()
|
|
(interactive)
|
|
(other-window -1)))
|
|
#+end_src
|
|
|
|
** Colour Scheme
|
|
Currently using =spacemacs-theme='s light variant, but I prefer a pure
|
|
white background to the off-white it has by default.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package spacemacs-theme
|
|
:defer t)
|
|
(setq spacemacs-theme-custom-colors
|
|
'((bg1 . "#ffffff")
|
|
(comment-bg . "#ffffff")))
|
|
(load-theme 'spacemacs-light t)
|
|
#+end_src
|
|
|
|
* Autocompletion
|
|
Enable =company-mode= globally, and hook it into =completion-at-point-functions=.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package company
|
|
:config
|
|
(add-hook 'after-init-hook 'global-company-mode)
|
|
(setf company-backends
|
|
(cons 'company-capf company-backends)))
|
|
#+end_src
|
|
|
|
And enable =ido-mode= everywhere, with flexible matching.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ido
|
|
:config
|
|
(setq ido-enable-flex-matching t)
|
|
(add-hook 'after-init-hook
|
|
(lambda ()
|
|
(ido-everywhere)
|
|
(ido-mode t))))
|
|
#+end_src
|
|
|
|
* Org
|
|
I use a couple non-standard bits and pieces, but not a whole
|
|
bunch. I really like the =<s= to insert a source block thing (which
|
|
was deprecated); =org-tempo= brings that back.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:ensure org-plus-contrib
|
|
:config
|
|
(require 'org-tempo))
|
|
#+end_src
|
|
|
|
A keybinding to add a new heading is super useful
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'org-mode-hook
|
|
(lambda ()
|
|
(define-key org-mode-map
|
|
(kbd "<C-M-return>")
|
|
'org-insert-heading-after-current)))
|
|
#+end_src
|
|
|
|
Org is nice for scratch space
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq initial-major-mode 'org-mode)
|
|
(setq initial-scratch-message "")
|
|
#+end_src
|
|
|
|
** Source Blocks
|
|
Pressing tab inside a source block should indent appropriately for its
|
|
language.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-src-tab-acts-natively t)
|
|
#+end_src
|
|
|
|
=babel= lets us evaluate Org documents containing source blocks!
|
|
I've left the enabling of this for most languages to the section
|
|
for that language, but I'll add Emacs Lisp and shell here.
|
|
|
|
#+begin_src emacs-lisp
|
|
(org-babel-do-load-languages
|
|
'org-babel-load-languages
|
|
'((emacs-lisp . t)
|
|
(shell . t)))
|
|
#+end_src
|
|
|
|
By default trying to execute a source block prompts you, which is
|
|
super annoying since I'm realistically not going to try to run any
|
|
code from Org documents I haven't written, so that needs
|
|
disabling. You can do that by setting [[help:org-confirm-babel-evaluate][org-confirm-babel-evaluate]] to
|
|
=nil=.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-confirm-babel-evaluate nil)
|
|
#+end_src
|
|
|
|
Another annoying thing that happens by default is the clobbering of
|
|
the window layout when you open a source block. You can change that
|
|
by setting [[help:org-src-window-setup][org-src-window-setup]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-src-window-setup 'split-window-below)
|
|
#+end_src
|
|
|
|
** Exporting
|
|
I very rarely want a table of contents, as most of my org documents
|
|
are pretty short.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-export-with-toc nil)
|
|
#+end_src
|
|
|
|
*** HTML
|
|
=htmlize= is needed for decent HTML exporting, but there is no need
|
|
for all that stuff at the bottom.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package htmlize)
|
|
(setq org-html-postamble nil)
|
|
#+end_src
|
|
|
|
*** LaTeX
|
|
Use =minted= (LaTeX package) to do syntax highlighting in code blocks:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'org-latex-packages-alist '("" "minted"))
|
|
(setq org-latex-listings 'minted)
|
|
#+end_src
|
|
|
|
=minted= actually calls =pygments= through the shell, which =pdflatex=
|
|
doesn't like; you have to tell it not to worry, and that everything is
|
|
going to be OK.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-latex-pdf-process
|
|
'("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
|
|
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
|
|
"xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
|
|
#+end_src
|
|
|
|
** Roam
|
|
A Zettelkasten in org mode? Yes please. It does need =sqlite3=
|
|
installed outside of Emacs land.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-roam)
|
|
#+end_src
|
|
|
|
As stated in [[info:org-roam#Getting Started][the manual]], org-roam needs to know where notes are
|
|
stored. I'm going to go with =~/org/zet=, as its nice having all
|
|
org documents under =~/org=, but also we need to distinguish
|
|
zettels from other org stuff.
|
|
|
|
#+begin_src emacs-lisp
|
|
(make-directory "~/org/zet" t)
|
|
(setq org-roam-directory "~/org/zet")
|
|
#+end_src
|
|
|
|
And, also as recommended, we'll start [[help:org-roam-mode][org-roam-mode]] after init:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'after-init-hook 'org-roam-mode)
|
|
#+end_src
|
|
|
|
Hook it into Ido.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-roam-completion-system 'ido)
|
|
#+end_src
|
|
|
|
** Default Applications
|
|
It's all fun and games until =C-c C-e h o= opens the source code.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq org-file-apps
|
|
'(("html" . "firefox %s")
|
|
(auto-mode . emacs)))
|
|
#+end_src
|
|
|
|
* Version Control
|
|
** Git
|
|
=magit= is truly a wonderful creation! Only deviations from defaults
|
|
here are a keybinding for =magit-status= and a maximum length for the
|
|
summary line of commit messages (after which the excess is
|
|
highlighted).
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package magit
|
|
:bind
|
|
("C-x g" . magit-status)
|
|
:config
|
|
(setq git-commit-summary-max-length 72))
|
|
#+end_src
|
|
|
|
* Language Integrations
|
|
Generally, 8-character-wide tabs are not my thing.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default tab-width 4)
|
|
(setq-default basic-offset 4)
|
|
#+end_src
|
|
|
|
And generally indenting with spaces is more common, so make that the default:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq-default indent-tabs-mode nil)
|
|
#+end_src
|
|
|
|
** C
|
|
For C, I like to indent with tabs and align with spaces: this
|
|
behaviour is provided by =smart-tabs-mode=.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package smart-tabs-mode)
|
|
(smart-tabs-insinuate 'c)
|
|
#+end_src
|
|
|
|
I'll generally format my code in BSD style but I also use
|
|
=clang-format= a lot, so I have a keybinding to run that.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq c-default-style "bsd")
|
|
(use-package clang-format)
|
|
(add-hook 'c-mode-hook
|
|
(lambda ()
|
|
(define-key c-mode-map (kbd "C-M-f")
|
|
'clang-format-buffer)))
|
|
#+end_src
|
|
|
|
Meson is my build system of choice for C, but I also use CMake a lot.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package meson-mode)
|
|
(use-package cmake-mode)
|
|
#+end_src
|
|
|
|
** Haskell
|
|
My workflow with Haskell is very REPL-based, so I always want
|
|
=interactive-haskell-mode= on.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package haskell-mode)
|
|
(require 'haskell-interactive-mode)
|
|
(add-hook 'haskell-mode-hook 'interactive-haskell-mode)
|
|
#+end_src
|
|
|
|
And, of course, that REPL needs to be taking advantage of parallelism!
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'haskell-process)
|
|
(set-variable 'haskell-process-args-ghci
|
|
'("-threaded" "+RTS" "-N8" "-RTS"))
|
|
#+end_src
|
|
|
|
** Idris
|
|
The only thing to change from the defaults here is to add a more
|
|
convenient way to case-split.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package idris-mode)
|
|
(add-hook 'idris-mode-hook
|
|
(lambda ()
|
|
(define-key idris-mode-map (kbd "C-c SPC")
|
|
'idris-case-split)))
|
|
#+end_src
|
|
|
|
** Rust
|
|
I never really use Rust without Cargo, so always turn on the minor
|
|
mode for Cargo in Rust buffers.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package rust-mode)
|
|
(use-package cargo)
|
|
(add-hook 'rust-mode-hook 'cargo-minor-mode)
|
|
#+end_src
|
|
|
|
** Lisps
|
|
|
|
*** Racket
|
|
Get =racket-mode= for some Racket-specific things, like searching documentation
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package racket-mode)
|
|
#+end_src
|
|
|
|
*** Common Lisp
|
|
Use SLIME and Quicklisp for Common Lisp (SBCL), with a convenient
|
|
binding for =slime-selector=
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package slime)
|
|
(setq inferior-lisp-program "sbcl")
|
|
(global-set-key (kbd "C-c s") 'slime-selector)
|
|
(load (expand-file-name "~/quicklisp/slime-helper.el"))
|
|
#+end_src
|
|
|
|
And we also want to enable execution of CL source blocks in Org
|
|
mode, which we do by adding an item to [[help:org-babel-load-languages][org-babel-load-languages]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(org-babel-do-load-languages
|
|
'org-babel-load-languages
|
|
'((lisp . t)))
|
|
#+end_src
|
|
|
|
*** Paredit
|
|
=paredit= is generally very useful for balancing parenthesis so we
|
|
want that turned on for all the lisps. Additionally, it's nice to have
|
|
an entire expression highlighted when the cursor is on one of its
|
|
enclosing parens.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package paredit)
|
|
(setq lispy-mode-hooks
|
|
'(emacs-lisp-mode-hook
|
|
lisp-mode-hook
|
|
racket-mode-hook
|
|
scheme-mode-hook
|
|
slime-repl-mode-hook))
|
|
(dolist (hook lispy-mode-hooks)
|
|
(add-hook hook (lambda ()
|
|
(setq show-paren-style 'expression)
|
|
(paredit-mode))))
|
|
#+end_src
|
|
|
|
** YAML
|
|
I don't really like YAML if I'm honest, but it's used a lot so...
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package yaml-mode)
|
|
#+end_src
|
|
|
|
** Javascript
|
|
The first bit of this setup (=js2-mode=, =js2-refactor= and
|
|
=xref-js2=) is essentially copied from [[https://emacs.cafe/emacs/javascript/setup/2017/04/23/emacs-setup-javascript.html][this Emacs cafe post]].
|
|
|
|
First of all we want to grab =js2-mode= and enable it for
|
|
javascript buffers. It extends the default =js-mode= and builds an
|
|
AST which can be used by other packages.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package js2-mode)
|
|
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
|
|
#+end_src
|
|
|
|
|
|
*** Refactoring
|
|
=js2-refactor= provides refactoring tools based of said AST, so
|
|
enable that and its keybindings:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package js2-refactor)
|
|
(add-hook 'js2-mode-hook #'js2-refactor-mode)
|
|
(js2r-add-keybindings-with-prefix "C-c C-r")
|
|
#+end_src
|
|
|
|
It provides a kill function with nice semantics for Javascript --
|
|
we definitely want that instead of the generic kill.
|
|
|
|
#+begin_src emacs-lisp
|
|
(define-key js2-mode-map (kbd "C-k") #'js2r-kill)
|
|
#+end_src
|
|
|
|
*** Find references / jump to definition
|
|
Then we get to =xref-js2=, which adds stuff for jumping to
|
|
references and definitions (uses the =ag= tool, so that must be
|
|
installed in the environment):
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package xref-js2)
|
|
#+end_src
|
|
|
|
=js-mode= binds =M-.=, which conflicts with =xref-js2= so we need to unbind that:
|
|
|
|
#+begin_src emacs-lisp
|
|
(define-key js-mode-map (kbd "M-.") nil)
|
|
#+end_src
|
|
|
|
And hook it up to =js2-mode=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'js2-mode-hook (lambda ()
|
|
(add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t)))
|
|
#+end_src
|
|
|
|
*** Using local tools from NPM
|
|
To use tools locally by NPM, there is =add-node-modules-path=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package add-node-modules-path)
|
|
(eval-after-load 'js2-mode
|
|
'(add-hook 'js2-mode-hook #'add-node-modules-path))
|
|
#+end_src
|
|
|
|
*** Autoformatting
|
|
[[https://prettier.io/][Prettier]] seems low-effort to set up :D
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package prettier-js)
|
|
(add-hook 'js2-mode-hook 'prettier-js-mode)
|
|
#+end_src
|
|
|
|
** Prolog
|
|
=prolog-mode= comes with Emacs, but .pl files are assumed to be
|
|
Perl (which I never use, it scares me), so we need to change that.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'auto-mode-alist '("\\.pl\\'" . prolog-mode))
|
|
#+end_src
|
|
|
|
* Desktop
|
|
** EXWM
|
|
One must fulfil the meme of doing everything with Emacs... still
|
|
got a lot of tweaking to do here before I'm happy.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package exwm
|
|
:config
|
|
(require 'exwm-config)
|
|
(exwm-config-default))
|
|
#+end_src
|
|
|
|
*** Multi-monitor
|
|
Multi-monitor support is provided in =exwm-randr=:
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'exwm-randr)
|
|
(exwm-randr-enable)
|
|
#+end_src
|
|
|
|
When I have my laptop connected to a monitor I want the built-in
|
|
display to turn off, but turn back on when it's disconnected. Turns
|
|
out this is a total pain.
|
|
|
|
To start with we need a function to tell whether a monitor's
|
|
attached. =exwm-randr= provides [[help:exwm-randr--get-monitors][exwm-randr--get-monitors]], but its
|
|
result is not -- as I'd expect -- a list of monitors, but instead a
|
|
rather complicated mess that is (as far as I can tell)
|
|
undocumented. Rather than trying to figure out what was going on
|
|
there, I opted for the search-in-shell-command-output route
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun hdmi-connected-p ()
|
|
(string-match-p "HDMI-2 connected"
|
|
(shell-command-to-string "xrandr")))
|
|
#+end_src
|
|
|
|
With that defined, an [[help:exwm-randr-screen-change-hook][exwm-randr-screen-change-hook]] can then be
|
|
added to turn the built-in display on and off appropriately.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'exwm-randr-screen-change-hook
|
|
(lambda ()
|
|
(let ((xrandr-command
|
|
(if (hdmi-connected-p)
|
|
"xrandr --output eDP-1 --off --output HDMI-2 --auto"
|
|
"xrandr --output eDP-1 --auto")))
|
|
(start-process-shell-command "xrandr" nil xrandr-command))))
|
|
#+end_src
|
|
|
|
** Mode Line
|
|
*** Clock
|
|
The time is a useful thing to know... and 12-hour clock is for
|
|
losers.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq display-time-24hr-format t)
|
|
(display-time-mode 1)
|
|
#+end_src
|
|
|
|
*** Battery
|
|
Also useful to know, but only on a laptop... once I'm using this
|
|
configuration on Mandarax as well I'll probably have to
|
|
conditionally disable it.
|
|
|
|
#+begin_src emacs-lisp
|
|
(display-battery-mode 1)
|
|
#+end_src
|
|
|
|
* Passwords
|
|
This was a little more work than I expected... =password-store=
|
|
provides a nice interface to =pass=, but annoyingly appears to
|
|
depend on =f= without declaring so.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package password-store)
|
|
(use-package f)
|
|
#+end_src
|
|
|
|
However, in order for it to actually work, EasyPG had to be
|
|
configured to use loopback for pinentry.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq epa-pinentry-mode 'loopback)
|
|
#+end_src
|
|
|
|
=gpg-agent= also had to be configured to allow loopback for
|
|
pinentry -- this was done by adding =allow-loopback-pinentry= to
|
|
[[file:~/.gnupg/gpg-agent.conf::allow-loopback-pinentry][gpg-agent.conf]].
|
|
|
|
With that all working, all that remains is to add a convenient
|
|
keybinding for getting a password, which is done by the function
|
|
=password-store-copy=. =C-c C-p= does conflict with /some/ modal
|
|
bindings, but I think that's fine as most of the time I'll need a
|
|
password it'll be in some X window anyway... and besides, =M-x
|
|
pass-co RET= isn't bad for when it does happen to conflict.
|
|
|
|
#+begin_src emacs-lisp
|
|
(global-set-key (kbd "C-c C-p") 'password-store-copy)
|
|
#+end_src
|
|
|
|
* Music Player
|
|
MPD is clearly the correct way to build a music player, so all
|
|
that's needed is a client for it... after having played around with
|
|
a couple of them, [[https://github.com/pft/mingus][Mingus]] is the one I've like the most.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package mingus)
|
|
#+end_src
|
|
|
|
* Mail
|
|
Currently using =mu4e= for mail. Not sure whether this is my 'final'
|
|
set up, I might give =notmuch= a try at some point.
|
|
|
|
=mu4e= is a bit annoying as it's bundled along with =mu= rather than
|
|
being loaded from ELPA or MELPA, so it can't be loaded with
|
|
=use-package=. Indeed, how to load it depends on how =mu= was
|
|
packaged. On NixOS at least, =mu4e= gets put in a place where Emacs
|
|
is able to find it, so it just needs to be =require='d.
|
|
|
|
#+begin_src emacs-lisp
|
|
(require 'mu4e)
|
|
#+end_src
|
|
|
|
I'm /sure/ this will break at some point; when it does, probably
|
|
makes sense to do something like:
|
|
|
|
#+begin_src emacs-lisp :tangle no
|
|
(let ((uname-output (shell-command-to-string "uname -a")))
|
|
(cond ((string-match-p "NixOS" uname-output) nil)
|
|
...))
|
|
(require 'mu4e)
|
|
#+end_src
|
|
|
|
To get the correct address by default:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq user-mail-address "cdo@wip.sh")
|
|
#+end_src
|
|
|
|
And to avoid being tickled:
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mail-host-address "hactar")
|
|
#+end_src
|
|
|
|
** Automatic updating
|
|
For updating through =mu4e= to actually work, [[help:mu4e-get-mail-command][mu4e-get-mail-command]]
|
|
needs to be set to =offlineimap=. New mail can be then fetched
|
|
with [[help:mu4e-update-mail-and-index][mu4e-update-mail-and-index]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-get-mail-command "offlineimap")
|
|
#+end_src
|
|
|
|
Sometimes (like when waiting for on a particular email) it might be
|
|
useful to have the update run periodically. This can be done with
|
|
[[help:run-with-timer][run-with-timer]]. By only actually updating if ~fetch-mail~
|
|
non-~nil~, we give ourselves a way to turn it off.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq mu4e-get-mail-command "offlineimap")
|
|
(defvar fetch-mail nil "Controls whether mail is periodically fetched.")
|
|
(run-with-timer 0 120 (lambda ()
|
|
(when fetch-mail
|
|
(mu4e-update-mail-and-index t))))
|
|
#+end_src
|
|
|
|
And then we need something to run through =M-x= to do that:
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun toggle-mail-fetching ()
|
|
"Toggle periodic mail fetching."
|
|
(interactive)
|
|
(setq fetch-mail (not fetch-mail))
|
|
(message "Mail fetching %s"
|
|
(if fetch-mail "enabled" "disabled")))
|
|
#+end_src
|
|
|
|
** Mode-line alert
|
|
=mu4e-alert= provides a convenient little icon that shows up
|
|
whenever =mu4e= has unread mail.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package mu4e-alert
|
|
:config
|
|
(add-hook 'after-init-hook
|
|
#'mu4e-alert-enable-mode-line-display))
|
|
#+end_src
|
|
|
|
** Sending with =sendmail=
|
|
I have =msmtp= set up so use that to send mail.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq send-mail-function 'sendmail-send-it)
|
|
#+end_src
|