Author: benmaughan

A shorter shortcut to capture todo tasks

Org mode is wonderful for managing todo lists. Usually to add a task, I would run org-capture using the default C-c c and then select my todo template by hitting t. I do this so often that I wanted a shorter shortcut, so I defined a function to call org-capture with the specific template I wanted and bound this to a simple shortcut (I used C-9 after removing numeric prefixes):

;; function to capture a todo
(defun bjm/org-capture-todo ()
  (interactive)
  "Capture a TODO item"
  (org-capture nil "t"))

;; bind
(define-key global-map (kbd "C-9") 'bjm/org-capture-todo)

Now C-9 takes me straight to my todo capture template.

Advertisement

An unobtrusive email monitor for mu4e on the Mac

This is not exactly an Emacs post, but some users of the mighty mu4e might find it useful. I don’t like big alerts to new emails that distract me from what I am doing, but I also don’t want to keep switching to my mu4e buffer to see if new emails have arrived. My solution is to have a small notification item in my Mac status bar to tell me how many emails I have.

It looks like this (the bit underlined in red):

email-counter2.png

The text @ 1/3/0/0 tells me that I have, respectively,

  • 1 unread email in my inbox.
  • 3 total emails in my inbox (i.e. 3 too many!).
  • 0 emails in my drafts folder. This is useful to keep an eye on in case I save a draft and then forget about it.
  • 0 emails in my outbox. This is my postfix email outbox and is useful to keep an eye on in case emails sent when I’m offline don’t get automatically sent when I reconnect.

To set this up I use shellwrangler which is a small app that embeds the output of a script in the status bar. Update 2017-11-10 shellwrangler appears dead and bitbar looks like a superior alternative. This then calls the following perl script which generates the text. Shellwrangler Bitbar updates at a specified interval so that is all there is to it.

#!/usr/bin/perl -w

###########################################################################
#
# Count number of emails in inbox, drafts and outbox for mail indexed
# with mu and sent with sendmail or equivalent
#
# by Ben Maughan
# http://www.pragmaticemacs.com
#
# Feel free to distribute, modify, whatever
#
###########################################################################

## total mails in inbox
chomp(my @tot=`/usr/local/bin/mu find maildir:/work/INBOX 2>/dev/null`);
## unread mails in inbox
chomp(my @unread=`/usr/local/bin/mu find maildir:/work/INBOX AND flag:unread 2>/dev/null`);
## drafts
chomp(my @drafts=`/usr/local/bin/mu find maildir:/work/[Gmail].Drafts 2>/dev/null`);

## number in outbox
chomp(my @mailq=`/usr/bin/mailq`);
my @outbox = grep /^[0-9A-F]{12}/, @mailq;

printf "@ %d/%d/%d/%d",$#unread+1,$#tot+1,$#drafts+1,$#outbox+1;

exit;

Reformatting Tabular Data

Sometimes in my research I need to extract tabular data from a pdf paper. I can copy and paste the table into an Emacs buffer but the data is generally not formatted in a usable way. Luckily Emacs has a wealth of tools to reformat this sort of data.

Here is an animated gif illustrating some tools I use to do this (of course there are lots of other ways to do the same thing).

reformat-table.gif

In the animation I use the following tools

  • C-x h to select the whole buffer.
  • C-c | to run org-table-create-or-convert-from-region to convert the region to an org table. This doesn’t get me all the way to where I want to be, but I find it helpful to see the data clearly.
  • M-S-<left> to delete some unwanted columns
  • mc/mark-next-like-this from multiple cursors to give me a cursor on each line (I bind this to M-.)
  • M-f to move forward by word
  • shrink-whitespace to remove whitespace (I bind this to M-SPACE)
  • C-c - to run org-table-insert-hline to add a nice horizontal line to my table

This restructures the data in the way I need, and I can now use org-table-export to export to other useful formats.

Save window layouts with ivy-view

The wonderful ivy library provides a command ivy-view which allows you to quickly bookmark the current arrangement of windows in your Emacs frame. The nice thing is that once you do this, the bookmarked arrangement then appears in your ivy-powered buffer switching list so changing back to the arrangement you had is as easy as switching buffers. This make a lightweight alternative to other mathods for managing window layouts.

To use this, just run ivy-push-view to store the current view, and optionally give it a name (a useful default we be offered). This will then be offered when you switch buffer using ivy-switch-buffer (which you are using automatically if you use ivy-mode). To make these ivy-views appear in your buffer list, you might need to set the option

(setq ivy-use-virtual-buffers t)

in you emacs config file.

Ivy author abo-abo gives an example on his blog post – take a look if this sounds useful.

Using email address lists in mu4e

To set up lists of multiple email multiple recipients with mu4e, you can make use of mail aliases. To do this, add lists to a file with the following format, with the word “alias” followed by the name you want for the list, followed by a space-separated list of email addresses:

alias jsmiths "John Q. Smith <none@example.com>" "Jane Q. Smith <email@address.com>"

You can either save this file as ~/.mailrc or put it anywhere and tell Emacs where to find it using e.g.

;;mail aliases for lists (address lists)
(setq mail-personal-alias-file (expand-file-name "/homeb/bjm/docs/mail-aliases"))

Now, when composing an email you can type the alias (“jsmiths” in our example) and hit space, and it will expand to the list of email addresses.

If you are using my improved address completion, then you can add those aliases to your bjm/contact-file to have them appear in your completion lists.

Make Emacs a bit quieter

The minibuffer is the area at the bottom of your Emacs frame where you interact with Emacs prompts, or type commands. It also doubles up as the echo area, where Emacs tells you useful things. However sometimes when Emacs is prompting me for an answer in the minibuffer, the prompt is hidden by a message so I can’t see what I’m being asked any more.

When this happens, I found it is almost always a message about a buffer being reverted or a file being auto-saved. These messages can be prevented from appearing in the echo area as follows.

The revert messages are easy to silence with the following setting:

;; turn off auto revert messages
(setq auto-revert-verbose nil)

For the auto-save messages, it is a bit trickier. Some solutions suggest advising do-auto-save which is the function that does the actual job of auto-saving (e.g. this stackexchange question). However, this doesn’t work for Emacs 24.4 and newer since the call to do-auto-save bypasses the advice, for technical reasons I don’t fully understand!

A work around is to switch off the built-in auto-save, and replace it with our own silent version:

;; custom autosave to suppress messages
;;
;; For some reason `do-auto-save' doesn't work if called manually
;; after switching off the default autosave altogether. Instead set
;; to a long timeout so it is not called.
(setq auto-save-timeout 99999)

;; Set up my timer
(defvar bjm/auto-save-timer nil
  "Timer to run `bjm/auto-save-silent'")

;; Auto-save every 5 seconds of idle time
(defvar bjm/auto-save-interval 5
  "How often in seconds of idle time to auto-save with `bjm/auto-save-silent'")

;; Function to auto save files silently
(defun bjm/auto-save-silent ()
  "Auto-save all buffers silently"
  (interactive)
  (do-auto-save t))

;; Start new timer
(setq bjm/auto-save-timer
      (run-with-idle-timer 0 bjm/auto-save-interval 'bjm/auto-save-silent))

Note that I found some strange behaviour that do-auto-save does not work if the built-in auto-save is switched off altogether using (setq auto-save-default nil), so instead I had to set it for a long timeout to prevent it from running.

Automatically revert buffers

If you want Emacs to automatically update a buffer if a file changes on disk, then add the following to your emacs config file

;; auto revert mode
(global-auto-revert-mode 1)

Of course, if your buffer has unsaved changes when the file changes on disk, then Emacs will prompt you and your changes won’t be lost.

This mode only applies to buffers associated with files on the disk, but I like to have my dired view updated if the contents of a directory change. This is accomplished with the following code:

;; auto refresh dired when file changes
(add-hook 'dired-mode-hook 'auto-revert-mode)

Speedy sorting in dired with dired-quick-sort

The package dired-quick-sort gives you a pop-up menu with lots of useful options to sort your dired view by name, time, size and extension, and optionally group all of the directories together at the top of the listing. This can be a bit fiddly and dired-quick-sort makes it really easy.

Install the package with

(use-package dired-quick-sort
  :ensure t
  :config
  (dired-quick-sort-setup))

and then hit S in a dired buffer to bring up the sorting menu. Your sorting choice is then remembered for new dired buffers.

Export org-mode headlines to separate files

I was recently managing a set of interviews and I had my notes on all of the candidates in a single org file, with each candidate under their own top-level headline:

* J Kepler
 - Nice work on orbits

* I Newton
 - Versatile
 - Hard to work with

* C Sagan
 - Good communication skills

However, I wanted to generate a separate pdf file for each candidate that I could circulate to interviewers (since each interviewer was only interviewing a subset of applicants).

I came across this stackexchange answer that demonstrated how to build a function to export top level headlines to separate files. There are a few variations on that page, and I put together the slightly tweaked version below. All of the credit goes to stackexchange user itsjeyd for a very detailed answer. In my version I hard code it to export to pdf, save the file first, and apply the export options from the parent file to each of the new files that are created. The new files have a name taken from the headline, with spaces replaced by underscores, unless the :EXPORT_FILE_NAME: property is set for a headline.

;; export headlines to separate files
;; http://emacs.stackexchange.com/questions/2259/how-to-export-top-level-headings-of-org-mode-buffer-to-separate-files
(defun org-export-headlines-to-pdf ()
  "Export all subtrees that are *not* tagged with :noexport: to
separate files.

Subtrees that do not have the :EXPORT_FILE_NAME: property set
are exported to a filename derived from the headline text."
  (interactive)
  (save-buffer)
  (let ((modifiedp (buffer-modified-p)))
    (save-excursion
      (goto-char (point-min))
      (goto-char (re-search-forward "^*"))
      (set-mark (line-beginning-position))
      (goto-char (point-max))
      (org-map-entries
       (lambda ()
         (let ((export-file (org-entry-get (point) "EXPORT_FILE_NAME")))
           (unless export-file
             (org-set-property
              "EXPORT_FILE_NAME"
              (replace-regexp-in-string " " "_" (nth 4 (org-heading-components)))))
           (deactivate-mark)
           (org-latex-export-to-pdf nil t)
           (unless export-file (org-delete-property "EXPORT_FILE_NAME"))
           (set-buffer-modified-p modifiedp)))
       "-noexport" 'region-start-level))))

Add multiple cursors with mouse clicks

I’ve written before about the wonderful multiple-cursors package. Usually I use it by hitting M-. (defined using the setup below) to fire off multiple cursors on successive lines or on successive occurrences of the current string (if some text is selected). I also use M-, if I want to remove some of those cursors. This covers 99% of my use of multiple cursors.

However, occasionally, the best way to get the cursors where you want them is with the mouse. With the following code, C-S-<left mouse click> adds a new cursor.

(use-package multiple-cursors
  :ensure t
  :bind (("M-." . mc/mark-next-like-this)
         ("M-," . mc/unmark-next-like-this)
         ("C-S-<mouse-1>" . mc/add-cursor-on-click)))