Month: August 2015

Fix touch-typing semicolon typo

I’ve started learning to touch type (for the nth time) and I find I make a lot of typos. Most are easily fixed with flyspell’s C-;, but I quite often hit ; instead of l, which flyspell can’t fix as it looks like the end of a word. I wrote a quick bit of code to replace the most recent ; with l

;;fix ; typos
(defun bjm-semicolon-to-l ()
  "Change the most recent semicolon behind the point to an l character. Useful for fixing a common touch-typing error"
  (interactive)
  (let ((bjm-start-pos (point)))
    (search-backward ";")
    (delete-char 1)
    (insert "l")
    (goto-char bjm-start-pos)))

(global-set-key (kbd "C-:") 'bjm-semicolon-to-l)

This is pretty simplistic, but it shows how easy it is to add a very personalised bit of extra functionality to emacs with minimal lisp skills!

Update

Grant Rettke pointed out in the comments that it is simpler to handle moving the point back to where you started by using save-excursion. The updated code is:

;;fix ; typos
(defun bjm-semicolon-to-l ()
  "Change the most recent semicolon behind the point to an l character. Useful for fixing a common touch-typing error"
  (interactive)
  (save-excursion
    (search-backward ";")
    (delete-char 1)
    (insert "l")))

Thanks Grant!

Advertisement

Jump back to previous typo.

We recently looked at using flyspell to quickly fix the most recent typo with C-;. Sometimes flyspell can’t find a correction for the word you want, so you need to fix it by hand.

Flyspell offers the function flyspell-goto-next-error which moves the point forward to the next error, and is bound to C-, (although this keybinding is taken over in org-mode). However, I only want to move back to the previous error, but flyspell offers no function for this. This is easily fixed with a very slightly modified version of the code from this answer by hatschipuh on stackexchange.

This code jumps you to the end of the most recent misspelled word.

;; move point to previous error
;; based on code by hatschipuh at
;; http://emacs.stackexchange.com/a/14912/2017
(defun flyspell-goto-previous-error (arg)
  "Go to arg previous spelling error."
  (interactive "p")
  (while (not (= 0 arg))
    (let ((pos (point))
          (min (point-min)))
      (if (and (eq (current-buffer) flyspell-old-buffer-error)
               (eq pos flyspell-old-pos-error))
          (progn
            (if (= flyspell-old-pos-error min)
                ;; goto beginning of buffer
                (progn
                  (message "Restarting from end of buffer")
                  (goto-char (point-max)))
              (backward-word 1))
            (setq pos (point))))
      ;; seek the next error
      (while (and (> pos min)
                  (let ((ovs (overlays-at pos))
                        (r '()))
                    (while (and (not r) (consp ovs))
                      (if (flyspell-overlay-p (car ovs))
                          (setq r t)
                        (setq ovs (cdr ovs))))
                    (not r)))
        (backward-word 1)
        (setq pos (point)))
      ;; save the current location for next invocation
      (setq arg (1- arg))
      (setq flyspell-old-pos-error pos)
      (setq flyspell-old-buffer-error (current-buffer))
      (goto-char pos)
      (if (= pos min)
          (progn
            (message "No more miss-spelled word!")
            (setq arg 0))
        (forward-word)))))

I bind this to C-, to replace flyspell-goto-next-error. See this stackexchange question for methods to ensure your keybinding overrides the bindings by major modes like org.

Org-mode basics: structuring your notes

I’ve been putting off writing about org-mode as it is hard to know where to start. What is org-mode? From the org-mode web page

Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.

For many people, org-mode is the reason they started using emacs. I only came to it after using emacs for about 10 years, but it was responsible for me moving from using emacs as a simple text editor to using emacs almost everywhere, and seeing a huge productivity boost.

Org-mode is very versatile, and I use it to (among other things):

  • Write general notes;
  • Write pdf lecture handouts and slides;
  • Write my research notes, analysis code, results, and final published papers in a single document allowing for reproducible research;
  • Manage my to do list and deadlines;
  • Write this blog;
  • Create static web pages;
  • Compose emails

One key thing to note is that while doing all these things, all org-mode documents are simple plain text that can be read in any text editor.

There are lots of org-mode tutorials out there such as

I don’t want to reinvent the wheel here, but I think the sheer amount that org-mode can do can be overwhelming for new users. So, in the spirit of this blog, I’ll write a series of posts to pick out some of the key features of org-mode that I use the most.

I’ll start here with the use of org-mode to make simple structured notes. This was the thing that got me hooked on org-mode, and everything else followed from here.

Org-mode is included in emacs, but you should install the most recent version (8.3.1 as of today).

The easiest way to get started is to open a new file in emacs with .org as the extension. Below is an example org document, and I would suggest typing this into your org-mode file to get a feeling for how the structure works.

* org-mode structure
Text in org is structured by headings, denoted by lines starting with
one or more * so we are currently in a section!

** A subheading
Starts with an extra * and so on

** navigation
Headings can be expanded or collapsed by moving to the (sub)heading
and pressing TAB. S-TAB cycles all headings. You can jump to next and
previous headings with C-c C-n and C-c C-p respectively.

You can move headings up and down to reorder them with the arrow keys,
using M-up or M-down. You can change the level of headings with M-left
and M-right (and use M-S-left and M-S-right to also change the levels
of and subheadings).

** lists
*** bullet lists
 - bullet lists can be created like this (start a line with one or
   more space and a -
 - pressing M-RET gives you a new bullet
 - we might also like nested bullets
   - like this one (I pressed M-RET TAB to indent it)
   - and another (M-RET now indents to the new level)
 - the nice thing is that for long lines of text, emacs wraps them
   so that they line up with the bullet
 - you can also reorder list items and change indentation using
   M-up or M-down just like with section headings
 - you can change bullet style using S-left and S-right

*** numbered lists
 1) numbered lists are also possible
 2) M-RET gives me a new number

*** checklists [/]
 - [ ] we can even have check lists
 - [ ] M-S-RET gives a new item with a check box
 - [ ] C-c C-c check/unchecks a box
 - [ ] you can have sub items
   + [ ] like this
   + [ ] that can be checked off individually
 - [ ] and you can track the number of items by adding [/] to the end
   of a line above a checklist - this updates when you check items off

*** definition lists
 - definition lists :: these are useful sometimes
 - item 2 :: M-RET again gives another item, and long lines wrap in a
      tidy way underneath the definition

I would suggest a couple of customisation to org-mode at this stage. Add the following to your emacs config file:

;; set maximum indentation for description lists
(setq org-list-description-max-indent 5)

;; prevent demoting heading also shifting text inside sections
(setq org-adapt-indentation nil)

That’s all for now. Try using org-mode to make simple notes and I think you’ll like the structure it gives you compared to simple text.

Dired: human-readable sizes and sort by size

We’ve looked a few times at dired, emacs’ powerful file browser. This time I want to look at file sizes in dired. By default, dired lists file sizes in bytes, but this is easy to change by customising the switches that dired uses when it calls your system’s ls command to generate the directory listing.

Add the following to your emacs config file to have file sizes given in “human-readable” format (i.e. in units of B, K, M, G as appropriate).

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; dired                                                                  ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(setq dired-listing-switches "-alh")

We already saw that hitting s in a dired window changes the sorting between file name and modification date. It is also nice to sort by file size sometimes. To do this, we can use the prefix argument in front of the s command, so use C-u s in the dired buffer. This prompts us for the ls switches, which we can change from -alh to -alhS to sort by size. Xah Lee covers this in more detail in his extensive emacs pages.

Deft as a file search tool

We’ve looked at using deft to make quick notes, with a couple of tweaks. I found I really like the deft interface for finding files. In particular, in the directory where I keep my blog posts, I have a large number of org files, and the deft interface is a very convenient way to search them to see in which posts I have mentioned particular things. I like the overview that deft gives you, and the incremental search as you type.

However, by default deft launches in my deft notes directory. To get around this I created a simple function to wrap around deft:

;;function to run deft in specified directory
(defun bjm-deft (dir)
  "Run deft in directory DIR"
  (setq deft-directory dir)
  (switch-to-buffer "*Deft*")
  (kill-this-buffer)
  (deft)
  )

This sets the desired directory to run deft, kills any current deft buffer (which may be in the wrong directory) and then starts deft.

I then bound this to convenient keys:

(global-set-key (kbd "C-c d")
                (lambda () (interactive) (bjm-deft "~/docs/deft")))
(global-set-key (kbd "C-c D")
                (lambda () (interactive) (bjm-deft "~/docs/emacs/blog")))

Delete blank lines and shrink whitespace

We looked previously at deleting spaces between words, and a related task is to delete one or more blank lines at the position of the cursor.

Emacs has a built-in command for this, delete-blank-lines, which is bound to C-x C-o. From the help for the function

On blank line, delete all surrounding blank lines, leaving just one. On isolated blank line, delete that one. On nonblank line, delete any immediately following blank lines.

However, the best solution to deleting both blank lines and spaces between words is to install the package shrink-whitespace which gives you the best of both worlds. As illustrated in this animation, if the cursor is between words it will delete spaces leaving one space, and calling it again will remove that space too (similar to cycle-spacing). If the cursor is between lines it will delete blank lines leaving one blank line, and calling it again will remove that line too.

delete-blank-lines.gif

I have bound this to M=SPACE in my emacs config file using

;; delete blank lines and whitespace
(global-set-key (kbd "M-SPC") 'shrink-whitespace)

Check and fix spelling as you type

To check spelling and highlight errors as you type, enable flyspell with M-x flyspell-mode. It has the incredibly useful feature that typing C-; will cycle through corrections for the most recent spelling error. This lets you correct typos without needing to move the cursor back to them. This become absolutely essential once you get used to it!

To enable flyspell automatically, add the following lines to your emacs config file.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; flyspell spell checking                                                ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;enable flyspell in text mode
(add-hook 'text-mode-hook 'flyspell-mode)
;;enable flyspell in org-mode
(add-hook 'org-mode-hook 'flyspell-mode)
;;enable for tex-mode
(add-hook 'latex-mode-hook 'flyspell-mode)
;;or if you use AUCTeX for latex
(add-hook 'LaTeX-mode-hook 'flyspell-mode)

Update

Commenter hmelman points out that we only need to enable flyspell for text-mode and it will be enables for org, latex and LaTeX modes since they derive from text mode:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; flyspell spell checking                                                ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;enable flyspell in text mode (and derived modes)
(add-hook 'text-mode-hook 'flyspell-mode)

Super spotlight search with counsel

Inspired by abo-abo‘s post on using his excellent tools counsel, ivy and swiper to search the contents of files indexed by the recoll search tool, I tried to make something similar for spotlight on the Mac. My attempt is below.

Using counsel gives us incremental updates of the spotlight search results (accessed using its command line interface mdfind). When a match is selected, it is opened in emacs, and (unless it is a pdf) a swiper search is launched on the search string.

This works really nicely. The only problem I’ve had is that I wanted to sort the results to prioritise .org and .tex files, but my sort function is not being used correctly in ivy, but I can’t tell why. It gets passed the counsel prompt for more characters, but not the set of mdfind matches for sorting. My lisp skills have been exhausted at this point, but maybe someone else can see what I’ve done wrong! UPDATE: this problem was fixed with an update to ivy and counsel, and the sorting command below now works.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; counsel-spotlight                                                      ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Incrementally search the Mac spotlight database and open matching
;; files with a swiper search on the query text.
;; Based on http://oremacs.com/2015/07/27/counsel-recoll/

(require 'counsel)

;; Function to be called by counsel-spotlight
;; The onlyin option limits results to my home directory
;; and directories below that
;; mdfind is the command-line interface to spotlight
(defun counsel-mdfind-function (string &rest _unused)
  "Issue mdfind for STRING."
  (if (< (length string) 4)
      (counsel-more-chars 4)
    (counsel--async-command
     (format "mdfind -onlyin ~/ '%s'" string))
    nil))

;; Main function
(defun counsel-spotlight (&optional initial-input)
  "Search for a string in the mdfind database.
You'll be given a list of files that match.
Selecting a file will launch `swiper' for that file.
INITIAL-INPUT can be given as the initial minibuffer input."
  (interactive)
  (ivy-read "spotlight: " 'counsel-mdfind-function
            :initial-input initial-input
            :dynamic-collection t
            :sort t
            :action (lambda (x)
                      (when (string-match "\\(\/.*\\)\\'" x)
                        (let ((file-name (match-string 1 x)))
                          (find-file file-name)
                          (unless (string-match "pdf$" x)
                            (swiper ivy-text)))))))

;; Define my sort function
(defun bjm-counsel-mdfind-sort-function (x y)
  "Compare two files X and Y. Prioritise org then tex."
  (if (string-match "org$" x)
      t
    (if (string-match "tex$" x)
        (if (string-match "org$" y)
            nil
          t)
      nil)))

;; Add to list of ivy sorting functions
(add-to-list 'ivy-sort-functions-alist
             '(counsel-mdfind-function . bjm-counsel-mdfind-sort-function))

In an ideal world, I’d like to be able to interactively narrow the matches from mdfind with a second counsel on the filenames. The selected file would then open with a swiper search for the first search term. For example, I would like to

  1. Call M-x counsel-spotlight and enter caustic (or enough characters to give me useful results) to get a list of names of files which contain the text caustic somewhere inside them.
  2. Hit some keybinding and get a new counsel prompt and enter chandra to incrementally filter the list of filenames to those containing the string chandra in the filename.
  3. Select the file I want and hit RET to have emacs open that with a swiper search of my original query caustic.

Given the state of my lisp skills, this might take me a while, but it is nice to dream!

Update: A cheat to sort/filter results

Given that my perl skills are much better than my lisp skills, I cheated and wrote a perl wrapper for mdfind that filters and sorts the results for me. The code is below – feel free to use and modify and share. To use this, save the code to a file called bjm-mdfind in your $PATH, make sure it has executable permissions set, and modify the lisp code above to use it:

;; Function to be called by counsel-spotlight
;;
;; mdfind is the command-line interface to spotlight
;;
;; The onlyin option limits results to my home directory
;; and directories below that
;;
;; N.B. below this is replaced with a custom perl wrapper to sort
;; and filter the mdfind results
(defun counsel-mdfind-function (string &rest _unused)
  "Issue mdfind for STRING."
  (if (< (length string) 4)
      (counsel-more-chars 4)
    (counsel--async-command
     (format "bjm-mdfind '%s'" string))
     ;;(format "mdfind -onlyin ~/ '%s'" string))
    nil))

Here is the perl code:

#!/usr/bin/perl -w

###############################################################################
# bjm-mdfind
#
# wrapper for mdfind to filter and sort results
# written by Ben Maughan http://pragmaticemacs.com/
#
# $Id: bjm-mdfind,v 1.2 2015/08/06 11:21:37 bjm Exp $
###############################################################################

use strict;
use File::Basename;
use Getopt::Long;

my $scriptname = basename($0); # Strip away the leading directory names
my $runtime = localtime;

##########################################################################
# customise these options                                                #
##########################################################################
# limit search to this dir (recursively)
my $dir="~/";
# preferred order of file extensions
# others will appear later
my @order=(".org",".tex",".el",".txt",".dat",".pdf");
# exclude files matching these strings
my @exclude=("Library/Caches","Application Support");

###############################################################################
# Handle command line arguments

my $help;
my $man;
my $v=0; #set default verbosity
my $version = defined ((split / /, q/$Revision: 1.2 $/)[1]) ? (split / /, q/$Revision: 1.2 $/)[1] : 0;
my @args=@ARGV;
my $nargs=1; #number of required command line args

&Getopt::Long::Configure( 'bundling' );
GetOptions(
           'help|h' => \$help,
           'man|m'  => \$man,
           'verbose|v=i' => \$v,
) or die "ERROR: Invalid command line option $!";

if ($help||$man||$#ARGV<$nargs-1) { #print help
  # Load Pod::Usage only if needed.
  require "Pod/Usage.pm";
  import Pod::Usage;
  pod2usage(VERBOSE => 1) if $help;
  pod2usage(VERBOSE => 2) if $man;
  pod2usage(VERBOSE => 0, -message => "ERROR: not enough arguments use --help or --man for more help") if $#ARGV<0;
  pod2usage(VERBOSE => 0, -message => "ERROR: not enough arguments - your input was:\n    $scriptname @args") if $#ARGV<$nargs-1;
}

#check input
my $string=$ARGV[0];

###############################################################################
# Main part of program

if ($v > 0) {
  print <<EOF

------------------------$scriptname version $version------------------

Invocation was:
$scriptname @args

Runtime $runtime

EOF
}

chomp(my @out=`mdfind -onlyin $dir $string`);

## sort and filter
##print "$order[2]\n\n";

##filter
my $exc=join "|", @exclude;
@out = grep !/$exc/, @out;

##sort
my @out2;
foreach my $ext (@order) {
    my @tmp = grep /$ext$/, @out;
    print "###$ext\n###@tmp\n\n" if $v>1;
    push @out2, @tmp;
}
##everything else
my $inv=join "\$|", @order;
print "###$inv\n" if $v>1;
my @rest = grep !/$inv$/, @out;
print "###@rest\n" if $v>1;
push @out2, @rest;

##join
my $out=join "\n", @out2;

##print
print "$out\n";

#report successful completion
print "$scriptname completed successfully\n\n" if $v>0;

###############################################################################
# POD documentation

=head1 NAME

bjm-mdfind

=head1 SYNOPSIS

B<bjm-mdfind> [options] string

=head1 DESCRIPTION

wrapper for mdfind to filter and sort results

  string        - search query

=head1 OPTIONS

=over 4

=item B<-h, --help>

Prints out a brief help message.

=item B<-m, --man>

Prints out detailed help.

=item B<-v, --verbosity>

Control the amount of output B<(Default = 1)>

=back

=head1 VERSION

This is $RCSfile: bjm-mdfind,v $ $Revision: 1.2 $

=head1 AUTHOR

Ben Maughan <benmaughan@gmail.com>

=cut

###############################################################################
# $Log: bjm-mdfind,v $
# Revision 1.2  2015/08/06 11:21:37  bjm
# Summary: added URL
#
# Revision 1.1  2015/08/06 11:17:37  bjm
# Initial revision
#