Emacs configuration

Table of Contents

Basics

Lexical binding

By default, Emacs uses dynamic binding. In order to use closures, I enable lexical binding.

;; -*- lexical-binding: t -*-

Color theme

I prefer a dark color theme. It’s much easier on the eyes, and I like working in the night often in dark rooms without any other lighting.

(load-theme 'manoj-dark)

Font size

Set the font size to something reasonable. The font size is in units of 1/10 pt. So, by setting :height to 120, I have set the font size to 12 pt. The default font face is what all other font faces base on.

tamil99 input method for Tamil text

I use the tamil99 input method to type Tamil text. But, I also use a dvorak keyboard sometimes, and this really messes up the key mapping of the tamil99 input method. So, I need to tell quail about my keyboard layout so it can correct its mapping.

(setq default-input-method "tamil99")

(with-eval-after-load 'quail
  (defun interleave-caps (str)
    "Interleave characters in STR with their capital forms."
    (mapconcat (lambda (c)
                 (string c (upcase c)))
               str ""))

  (setq quail-keyboard-layout-alist
        (map-insert quail-keyboard-layout-alist
                    "programmer-dvorak"
                    (let ((rows 6)
                          (columns 15))
                      (concat
                       ;; No row 1
                       (make-string (* 2 columns) ?\s)
                       ;; Row 2
                       "  1!2@3#4$5%6^7&8*9(0)" (make-string (* 2 4) ?\s)
                       ;; Row 3
                       "  ;:,<.>" (interleave-caps "pyfgcrl") "/?\\|" (make-string (* 2 2) ?\s)
                       ;; Row 4
                       "  " (interleave-caps "aoeuidhtns") "-_" (make-string (* 2 3) ?\s)
                       ;; Row 5
                       "  '\"" (interleave-caps "qjkxbmwvz    ")
                       ;; No row 6
                       (make-string (* 2 columns) ?\s)))))

  ;; Uncomment whichever I need.
  (quail-set-keyboard-layout "programmer-dvorak")
  ;; (quail-set-keyboard-layout "standard")
  )

Package manager and repositories

First things first, set up the package manager. Define the list of repos and initialize installed packages. More and more, I only use Emacs packages from my distro, GNU GuixSD. But, from time to time, I still need MELPA.

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)

Autoloading

Autoloading lets you speed up Emacs startup and reduce memory usage by postponing loading files until the last moment when some command requiring that file is invoked.

Wherever necessary, I have wrapped up forms in the built-in with-eval-after-load macro so that they run only after some file is loaded.

Enabling disabled commands

Emacs starts with, several commands that would be confusing to the uninitiated, disabled. We enable some of them.

(put 'erase-buffer 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)

No tabs

Indent using spaces instead of tabs. Since indent-tabs-mode is a buffer-local variable, set its default value. If we set its value using setq, it only applies to the current buffer.

(setq-default indent-tabs-mode nil)

Single space sentences

Emacs defaults to an antiquated typewriter practice whereby sentences where separated by two spaces. Disable this to use the more intuitive modern practice of a single space

(setq sentence-end-double-space nil)

Better integration with the clipboard

Emacs’ kill ring is distinct from the system clipboard. Emacs does a decent job of integrating the two and pretending they’re the same. But there’s one annoying quirk—when you copy something from an external program (say, a web browser) and you kill without yanking, the text from the external program is forever lost. This is an itty-bitty detail, but the consequences are real and annoying. We fix this here. Sometimes, Emacs really lacks sensible defaults!

(setq save-interprogram-paste-before-kill t)

Majority world calendar date style

Use the sensible day/month/year date style that the majority of the world uses.

(setq calendar-date-style 'european)

Prevent killing of the scratch buffer

One should never be able to accidentally kill the scratch buffer; prevent it. Prevention is better than cure.

(with-current-buffer "*scratch*"
  (emacs-lock-mode 'kill))

Backup files

I don’t want backup files littering my folders. I use version control systems a lot anyway.

(setq make-backup-files nil)

Menu and tool bars

Coming from a Vim background, I prefer to not have menu bars and tool bars clutter my limited screen real estate. I like all of the screen devoted to just text. Menu bars are very useful to discover new features, but I can always enable them when I need to, instead of keeping them permanently enabled.

(menu-bar-mode 0)
(tool-bar-mode 0)

Visual line mode

A lot of my work in emacs involves writing using org mode and latex. Word wrapping and the use of “visual lines” is absolutely important for this.

(global-visual-line-mode t)

Highlighting parentheses

Highlighting matching parentheses is really important when coding. I don’t know why this is not enabled by default in emacs.

(show-paren-mode)

Modeline

Emacs’ default modeline is positively ugly. Here’s a decent one—smart-mode-line.

(setq sml/theme 'dark
      sml/no-confirm-load-theme t)
(sml/setup)

Windows

Moving between windows

The standard way of moving between windows using C-x o involves too many keystrokes. I prefer the more spatially intuitive s-{left,right,up,down} keybindings of the Windmove package with wrapping around the edge of the frame enabled. The Windmove default “Shift” modifier conflicts with org mode’s default keybindings. So, I’m using the “Super” modifier instead.

(setq windmove-wrap-around t)
(windmove-default-keybindings 's)

Window manipulation keybindings

I bind several window and buffer manipulation commands to keybindings in the spirit of the Super key windmove keybindings. The keybindings I have set up are better than the default keybindings for these commands because they only need one keypress instead of two.

I hardly use the window enlargement/shrinking commands. Nevertheless, I keep them just in case I need them.

(exwm-input-set-key (kbd "<s-tab>") 'switch-to-last-used-buffer)
(exwm-input-set-key (kbd "s-_") 'split-window-below)
(exwm-input-set-key (kbd "s-|") 'split-window-right)
(exwm-input-set-key (kbd "s--") 'shrink-window-horizontally)
(exwm-input-set-key (kbd "<s-kp-subtract>") 'shrink-window-horizontally)
(exwm-input-set-key (kbd "s-+") 'enlarge-window-horizontally)
(exwm-input-set-key (kbd "<s-kp-add>") 'enlarge-window-horizontally)
(exwm-input-set-key (kbd "s-*") 'enlarge-window)
(exwm-input-set-key (kbd "<s-kp-multiply>") 'enlarge-window)
(exwm-input-set-key (kbd "s-/") 'shrink-window)
(exwm-input-set-key (kbd "<s-kp-divide>") 'shrink-window)

Completion

For completion, I use the minimalistic completion packages—vertico and corfu—that integrate tightly with Emacs’ default completion system. Once upon a time, I was using Helm, but have since given it up to reduce complexity. With vertico and corfu, I only configure Emacs’ default completion system, and it gets used everywhere.

(vertico-mode)
(global-corfu-mode)

I use the orderless completion style.

(setq completion-styles '(orderless))

I don’t want Guile’s object files cluttering my find-file completions. So, I added the .go extension to completion-ignored-extension.

(add-to-list 'completion-ignored-extensions ".go")

marginalia provides short annotations for completions. Not only is this pretty, it is quite useful as well. For example, when looking up elisp variables or functions with describe-variable and describe-function, it is nice to see their docstring summary before selecting them. Likewise, when browsing through commands with M-x, it’s nice to a short description.

(marginalia-mode)

Consult

I use consult-buffer and consult-line everyday to jump around my buffers. consult-imenu is handy as well.

(exwm-input-set-key (kbd "s-b") 'consult-buffer)
(global-set-key (kbd "C-s") 'consult-line)
(global-set-key (kbd "C-<menu>") 'consult-imenu)

consult-ripgrep is incredibly useful to search around a large number of files. The live previews are invaluable. I alias it to rg for short.

(defalias 'rg 'consult-ripgrep)

Embark

Embark is handy composability.

(global-set-key (kbd "C-.") 'embark-act)

Emacs as a window manager

I totally live in Emacs, so much so that I run exwm on Emacs to use it as my window manager. This is, at least at the moment, not the easiest or the most reliable approach to window management, but there are benefits. For example, with Emacs as my window manager, I can bind any key sequence on my keyboard in Emacs. Other window managers tend to block some key sequences. I can switch to and from external programs as though they were just Emacs buffers. I don’t need an external program such as xbindkeys to handle keybindings to media keys, function keys, etc. I can instead use the excellent programmability of Emacs Lisp combined with clean integration to the exwm window manager. Not only does this centralize more of my system configuration in one place, it also lets me write more complex and cleanly integrated functions for my keybindings than would be possible with a bunch of shell scripts.

Display the battery and average system load

In the absence of a full fledged desktop environment, it is still nice to see the current battery and average system load (but not the time—see Clock-free life). Let’s activate display-battery-mode and display-time.

(display-battery-mode)
(display-time)

Bind EXWM keys

In order to get keybindings to work across all buffers (both external program buffers managed by exwm and other Emacs buffers), I bind them using exwm-input-set-key.

I might have to bind key sequences to not just Emacs commands, but also to external programs. For this, the external program needs to be wrapped up in an Emacs command. This wrapping is handled by make-external-command. make-external-command returns a command that invokes the external program if it isn’t already running, switches to the buffer of the external program if it is already running, and switches to the last used buffer1 if the external program is already running and is the current buffer. This makes it easy to start an external program, switch to it, and switch back from it all using the same keybinding. Similarly, we have start-shell to switch back and forth between a shell and some other buffer.

(require 'exwm)

(defun switch-to-last-used-buffer ()
  (interactive)
  (switch-to-buffer (other-buffer (current-buffer) t)))

(defun make-external-command (command)
  (lambda ()
    (interactive)
    (let ((buffer-name (car (split-string command))))
      (cond
       ((equal buffer-name (buffer-name))
        (switch-to-last-used-buffer))
       ((get-buffer buffer-name)
        (switch-to-buffer (get-buffer buffer-name)))
       (t (start-process-shell-command buffer-name nil command))))))

(defun start-shell ()
  (interactive)
  (if (equal (buffer-name) "*shell*")
      (switch-to-last-used-buffer)
    (shell)))

Binding function keys and media keys

I then bind a lot of external programs to the function and media keys.

(exwm-input-set-key (kbd "<XF86MonBrightnessDown>")
                    (make-external-command "light -U 5"))
(exwm-input-set-key (kbd "<XF86MonBrightnessUp>")
                    (make-external-command "light -A 5"))
(exwm-input-set-key (kbd "<XF86AudioMute>")
                    (make-external-command "amixer set Master toggle"))
(exwm-input-set-key (kbd "<XF86AudioLowerVolume>")
                    (make-external-command "amixer set Master 5%-"))
(exwm-input-set-key (kbd "<XF86AudioRaiseVolume>")
                    (make-external-command "amixer set Master 5%+"))
(exwm-input-set-key (kbd "<XF86HomePage>")
                    (make-external-command "icecat"))
(exwm-input-set-key (kbd "<XF86Sleep>")
                    (make-external-command "loginctl suspend"))
(exwm-input-set-key (kbd "<S-XF86Sleep>")
                    (make-external-command "loginctl suspend; slock"))
(exwm-input-set-key (kbd "s-1") 'delete-other-windows)
(exwm-input-set-key (kbd "s-2") (make-external-command "nyxt"))
(exwm-input-set-key (kbd "s-3") 'start-shell)
(exwm-input-set-key (kbd "<menu>") 'execute-extended-command)
(exwm-input-set-key (kbd "s-f") 'find-file)
(exwm-input-set-key (kbd "s-k") 'kill-buffer)

Running external programs

I might still need to run external programs not covered above with the function and media keys. For these, I have a command run-external-program that prompts for the program to run and starts it using start-process-shell-command. The prompt provides all executables in PATH as completions. When building a list of completions, care is taken to filter out non-existent directories in PATH.

(let ((executables (seq-mapcat (lambda (bin)
                                 (when (file-exists-p bin)
                                   (directory-files bin nil (rx string-start (not ?.)))))
                               exec-path)))
  (defun run-external-program (command)
    (interactive (list (completing-read "$ " executables)))
    (start-process-shell-command command nil command)))

(exwm-input-set-key (kbd "s-r") 'run-external-program)

Rename exwm buffers

I want exwm buffers to be named according to the programs they are running.

(add-hook 'exwm-update-class-hook
          (lambda ()
            (exwm-workspace-rename-buffer
             (downcase exwm-class-name))))

Enable exwm

Finally, I enable exwm.

(exwm-enable)

exwm-edit

exwm-edit is a really handy EXWM extension that lets you edit any text area in any program using Emacs. This means I get to use Emacs even when filling out boring web forms and I get to use the Emacs tamil99 input method everywhere without having to install something like ibus.

(require 'exwm-edit)
(global-exwm-edit-mode)

Major modes

Programming modes

In programming modes, it’s useful to know how close to the edge you are. The display-fill-column-indicator-mode adds a line showing you the edge.

(add-hook 'prog-mode-hook 'display-fill-column-indicator-mode)

Lisps

For the lisps, after trying many structured editing packages, I have returned to the humble paredit and Emacs’ own S-expression editing commands.

(mapc (lambda (hook)
        (add-hook hook 'paredit-mode))
      (list 'emacs-lisp-mode-hook 'lisp-interaction-mode-hook
            'lisp-mode-hook 'lisp-data-mode-hook 'scheme-mode-hook))

Skribilo

In addition, we need the skribilo minor mode for skribilo files.

(setf (map-elt auto-mode-alist (rx ".skb" string-end) nil 'equal)
      'skribilo-mode)

Ledger

I keep accounts with ledger. Here, I tell Emacs to automatically open .ledger files in ledger-mode.

(add-to-list 'auto-mode-alist '("\\.ledger" . ledger-mode))

Gnuplot mode

Gnuplot mode provides syntax highlighting, indentation and a few other convenient functions for editing gnuplot scripts. Unfortunately, by default, gnuplot mode only recognizes file extensions .gp and .gnuplot as gnuplot scripts, while Vim recognizes the .gpi extension. I don’t want to have to rename all my gnuplot scripts. So, I just tell gnuplot mode to recognize the .gpi extension as well.

(add-to-list 'auto-mode-alist '("\\.gpi" . gnuplot-mode))

Octave mode

Octave mode is a major mode for editing Octave programs. I set Octave mode to recognize .m files as Octave source files. Without this, .m files are opened in Objective C mode, which I have no need for.

(map-put auto-mode-alist "\\.m$" 'octave-mode)

Markdown modes

In my markdown modes, I prefer soft line breaks with automatic word wrapping. I used to love fill-paragraph and M-q was deeply ingrained in my muscle memory. But, I’ve outgrown it now, and instead use visual-fill-column-mode to automatically soft-wrap text.

(with-eval-after-load 'org
  (add-hook 'org-mode-hook 'visual-fill-column-mode))

(with-eval-after-load 'gemini-mode
  (add-hook 'gemini-mode-hook 'visual-fill-column-mode))

(with-eval-after-load 'markdown-mode
  (add-hook 'markdown-mode-hook 'visual-fill-column-mode))

Org Tempo provides “structure templates” for SRC blocks among other things. Very handy since I find myself creating SRC blocks all the time.

(require 'org-tempo)

Email

notmuch email client

Set notmuch as the mail user agent.

(setq mail-user-agent 'notmuch-user-agent)

Encryption

Email encryption can sure do with some assistance from automation. Without automation, it is easy to forget to encrypt.

When encrypting outgoing emails, I also encrypt to myself so that I can read my own archives later. If I didn’t, I would be left with opaque encrypted blobs in my archives.

(setq mml-secure-openpgp-encrypt-to-self t)

Configure Emacs to automatically encrypt when both the following are true:

  • I have a private key for the email address I am sending from
  • I have the public keys of all my correspondents
(defun encrypt-when-possible ()
  (when (and (secret-key-available-p
              (canonical-email-address (message-fetch-field "From")))
             (message-all-epg-keys-available-p))
    (mml-secure-message-sign-encrypt)))

(add-hook 'message-send-hook 'encrypt-when-possible)

And, here’s the implementation of secret-key-available-p.

(defun canonical-email-address (address)
  "Return the canonical address part of email ADDRESS.

For example, (canonical-email-address \"Foo <foo@example.com>\")
=> \"foo@example.com\""
  (pcase (mail-extract-address-components address)
    (`(,_ ,canonical-address) canonical-address)))

(defun secret-key-available-p (email-address)
  "Check if secret key is available for EMAIL-ADDRESS.

Return non-nil if a valid secret key is available for
EMAIL-ADDRESS. EMAIL-ADDRESS is a canonical email address without
the name."
  (seq-some (lambda (key)
              (seq-some (lambda (user-id)
                          (and (string= (canonical-email-address
                                         (epg-user-id-string user-id))
                                        email-address)
                               (not (eq (epg-user-id-validity user-id)
                                        'revoked))))
                        (epg-key-user-id-list key)))
            (epg-list-keys (epg-make-context epa-protocol)
                           email-address t)))

Earlier, I used to automatically sign all outgoing emails regardless of whether my correspondents knew about PGP. This ended up confusing and maybe even annoying some people since they would mistake the signatures for attachments. So, I have stopped doing this. Nevertheless, here is the function if anyone needs it.

(defun sign-or-encrypt-when-possible ()
  (when (secret-key-available-p
         (canonical-email-address (message-fetch-field "From")))
    (if (message-all-epg-keys-available-p)
        (mml-secure-message-sign-encrypt)
      (mml-secure-message-sign))))

Email templates

Translation project robot

I sometimes translate for the Translation Project and have to send in PO files in a specific format by email. So, I keep an elisp function handy.

(defun compose-mail-to-translation-project-robot (po-file)
  (interactive "f")
  (compose-mail "robot@translationproject.org"
                (file-name-nondirectory po-file))
  (goto-char (point-max))
  (newline)
  (mml-attach-file (expand-file-name po-file)
                   "text/plain" nil "inline"))

Seminar emails at the UCL Genetics Institute (UGI)

At work in University College London, I have volunteered to send out seminar emails every week—a very repetitive task worth automating.

(defun compose-ugi-seminar-email (speaker when title abstract)
  (interactive (list (read-string "Speaker: ")
                     (org-read-date nil t nil "When?")
                     (read-string "Title: ")
                     (read-string "Abstract: ")))
  (let ((date-string
         (let ((system-time-locale "C"))
           (string-join (list (format-time-string "%A" when)
                              (string-trim (format-time-string "%d" when) "0+")
                              (format-time-string "%B" when))
                        " "))))
    (compose-mail "ugi-seminar@ucl.ac.uk, gee-seminars@ucl.ac.uk"
                  (format "UGI seminar, %s — %s: '%s'"
                          date-string
                          speaker
                          title)
                  `(("From" . ,(message-make-from
                              (notmuch-user-name) my-ucl-email))))
    (insert (format "
Dear all,

The next UGI seminar will be given by %s. Please see details below.

🧑‍🔬 Speaker: %s
🕛 When: %s (12 noon – 1 pm), followed by lunch
📍 Where: Gertrude Falk room

Title: %s

Abstract:

%s

Best wishes,
Arun on behalf of the organizers
"
                    speaker
                    speaker
                    date-string
                    title
                    abstract))))

Guix

Guix store paths have long ugly hashes, and we don’t usually want to see them. So, hide them with guix-prettify-mode.

(global-guix-prettify-mode)

guix-scheme-mode is a little-known gem that can indent Guix-generated program-file scripts in the store. Unfortunately, there is no good way to auto-detect such scripts and automatically enable guix-scheme-mode. Here’s a hack.

(require 'guix)
(require 'guix-ui-store-item)

(defun longest-line-length ()
  "Return the length of the longest line.

As a side-effect, this moves the point. Wrap in `save-excursion'
to avoid this."
  (max (- (line-end-position)
          (line-beginning-position))
       (if (zerop (forward-line))
           (longest-line-length)
         0)))

(defun maybe-guix-scheme-mode ()
  "Enable `guix-scheme-mode' on program-file scripts in the store."
  (unless (eq major-mode 'guix-scheme-mode)
    (when (and (buffer-file-name)
               (string-match guix-store-file-name-regexp
                             (buffer-file-name))
               ;; program-file scripts are often one very long line.
               ;; Use this as a heuristic to detect them.
               (> (save-excursion
                    (longest-line-length))
                  200))
      (guix-scheme-mode))))

(add-hook 'scheme-mode-hook 'maybe-guix-scheme-mode)

Emacs as a file manager

Dired

With its convenient emacsy interface and good Emacs integration, Dired is the text based file manager that finally convinced me to quit the GUI file managers I was using earlier. I spend so much time in Emacs (programming, mail, browsing, blogging, getting things done with org mode, etc.) that an Emacs based file manager is just the natural thing to have!

In Dired’s file listings, I prefer seeing approximate human readable file sizes (such as 1K, 234M, 2G, etc.) instead of the number of bytes which are usually too large to be meaningfully interpreted by a human being. Hence I’ve added the -h switch to Dired’s ls calls.

(with-eval-after-load 'dired
  (setq dired-listing-switches "-alh"))

openwith mode

Likewise, I want to be able to open files using the right program automagically like GUI file managers do. Hence, I set up openwith-mode with my preferred file-program associations.

(openwith-mode)

(setq openwith-associations
      `((,(rx (or ".djvu" ".eps" ".pdf" ".ps") string-end)
         "zathura" (file))
        (,(rx (or ".aac" ".avi" ".m4a" ".mkv" ".mp3" ".mp4" ".ogv" ".webm")
              (optional ".part")
              string-end)
         "mpv" (file))
        (,(rx (or ".doc" ".docx"
                  ".odt" ".ods"
                  ".ppt" ".pptx"
                  ".xls" ".xlsx")
              string-end)
         "libreoffice" (file))))

Following feeds with elfeed

I use elfeed to keep track of feeds. You need to configure the feeds you are following in elfeed-feeds. My real list is private. But, here’s a dummy one.

(with-eval-after-load 'elfeed
  (setq elfeed-feeds
        (list "https://planet.guix.gnu.org/atom.xml"
               "https://hpc.guix.info/blog/feed.xml"
               "https://guix.gnu.org/feeds/blog.atom")))

That apart, I also like the idea of using native elisp functions like url-retrieve. So, I tell elfeed not to use curl.

(with-eval-after-load 'elfeed
  (setq elfeed-use-curl nil))

Reading EPUB books using nov-el

I prefer paper and don’t read EPUB books much. But, when I have to, I use nov.el. It’s much easier than having a separate heavy GUI program for it. Here, I instruct Emacs to automatically open .epub files with nov-mode. And, I set a reasonable text width.

(map-put auto-mode-alist "\\.epub$" 'nov-mode)
(setq nov-text-width 60)

Emacs Multimedia System

The Emacs Multimedia System (EMMS) lets you play audio/video from Emacs. The actual playing is delegated to external players. EMMS merely holds the controls, handles the media player user interface, etc. I use mpg321 for mp3, ogg123 for ogg.

(with-eval-after-load 'emms
  (require 'emms-source-file)
  (require 'emms-player-simple)

  (setq emms-player-list '(emms-player-mpg321 emms-player-ogg123)))

Dictionary

I run the Dico RFC 2229 dictionary server locally, and it’s nice to read it from the comfort of my Emacs. I wish the Oxford English Dictionary would make their data available for offline use (I’m more than willing to pay for it), but they’ve switched to an extractive online subscription offering instead. Sigh.

(with-eval-after-load 'dictionary
  (setq dictionary-server "localhost"))

Useful minor modes

which-function-mode

(setq which-func-modes
      (list 'emacs-lisp-mode 'latex-mode 'scheme-mode))
(which-function-mode)

Andrews & Arnold SMS

The excellent Andrews & Arnold is my mobile service provider. That means I get to send SMS through their SMS API. And, how better to send SMS than through Emacs! BTW, did you know that Ofcom has allocated the mobile number range 07700 900000 to 07700 900999 for use as fictional numbers in TV and radio drama programmes? Wow, the bureaucrat’s attention to detail!

;; Dummy phone number
(defvar my-phone-number "07700900000"
  "My phone number for AA SMS.")

;; Dummy phonebook
(defvar sms-phonebook
  '(("Alice"  . "07700900001")
    ("Bob"    . "07700900002")
    ("Carol"  . "07700900003"))
  "Alist mapping contact names to phone numbers.")

(defun aa-sms (to message)
  (interactive (list (completing-read "To: "
                                      (map-keys sms-phonebook)
                                      nil t)
                     (read-string "Message: ")))
  (let ((url-request-method "POST")
        (url-request-extra-headers
         '(("Content-Type" . "application/x-www-form-urlencoded")))
        (url-request-data
         (url-build-query-string
          `((username ,my-phone-number)
            (password ,(password-store-get "london/aa-isp/sip"))
            (da ,(map-elt sms-phonebook to))
            (ud ,message)))))
    (url-retrieve-synchronously "https://sms.aa.net.uk/sms.cgi")
    (message "%sக்குக் குறுஞ்செய்தி அனுப்பப்பட்டது" to)))

I’ve also configured Andrews & Arnold to send a copy of all SMS messages to my email. When they show up in my notmuch-show message buffers, I replace the phone numbers with names from the phonebook.

(defun rlookup-sms-phonebook (phone-number)
  (pcase (rassoc phone-number sms-phonebook)
    (`(,name . ,phone-number) name)))

(defun substitute-sms-phone-number-hook ()
  (when (string= (canonical-email-address (notmuch-show-get-from))
                 "sms@aa.net.uk")
    (save-excursion
      (goto-char (point-min))
      (when (re-search-forward (rx "SMS from " (group (group (= 5 digit)) " " (group (= 6 digit)))
                                   " to " (group (group (= 5 digit)) " " (group (= 6 digit))))
                               nil t)
        ;; notmuch-show buffers are read-only. Inhibit it temporarily
        ;; so we can mutate it.
        (let ((inhibit-read-only t))
          (replace-match (rlookup-sms-phonebook (concat (match-string 2)
                                                        (match-string 3)))
                         nil nil nil 1)
          (replace-match (rlookup-sms-phonebook (concat (match-string 5)
                                                        (match-string 6)))
                         nil nil nil 4))))))

(add-hook 'notmuch-show-hook 'substitute-sms-phone-number-hook)

Clocks and time

Clock-free life

I live a clock-free life outside the tyranny of the clock as much as I can reasonably help it. I don’t want to know the precise hour and minute of every moment of the day. It is much less stressful to live by imprecise natural time afforded by the movement of the Sun. I therefore don’t want to have a clock on my modeline, although I’d still like to see the average system load. So, set the time format of display-time-mode to the empty string.

(setq display-time-format "")

Time across the world

Collaborating across timezones is complicated and subject to the mysteries of daylight savings. I use world-time-mode, for which I need to configure all the timezones I am interested in in display-time-world-list.

(setq display-time-world-list
      `(("Europe/London" "இலண்டன்")
        ("Asia/Kolkata" "பெங்களூர்")
        ("Africa/Nairobi" "நைரோபி")
        ("America/Chicago" "மெம்பிசு")
        ("America/New_York" "புதிய யோர்க்")
        ("America/Los_Angeles" "இலாஸ் ஏஞ்சலெஸ்")
        ("Europe/Amsterdam" "ஆம்சுடர்டாம்")
        ("Europe/Paris" "பாரிசு")
        ("Europe/Athens" "ஆதென்சு")
        ("Asia/Jerusalem" "இயெருசலேம்")))

Footnotes:

1

The switch-to-last-used-buffer function that I use for this, I credit to http://emacsredux.com/blog/2013/04/28/switch-to-previous-buffer/