Helm provides so many great commands I am still discovering new gems in it, like the useful helm-regexp, which lets you build your regular expression pattern, see the results of your search live in the Helm buffer, and optionally pass the query to query-replace-regexp. So neat.

This is why I haven’t written a Helm command of my own until recently. To be fair, as I am going to show there is not even the need for this command to exist, but coding it has been a fun exercise anyway.

When it comes to spell-checking my buffers, I have been relying on mu-cycle-ispell-languages to change dictionaries and flyspell-correct-helm to pick the correct word I happened to misspell. Since I am already using Helm with Flyspell, then, why not using it to select the dictionary too?

First, the actual dictionaries.

(defvar mu-dictionaries '(("en_GB" . "English")
("it_IT" . "Italian")))


Note that I use hunspell for spell-checking as I explained in Spell-checking with Hunspell and flyspell-correct.

Next, my new command.

(defun mu-select-dictionary ()
"Select the dictionary for the spell-checker."
(interactive)
(helm :sources
(helm-build-sync-source "Dictionaries"
:candidates (map-values mu-dictionaries)
:action (lambda (candidate)
(let ((dict (car (rassoc candidate mu-dictionaries))))
(ispell-set-spellchecker-params)
(setq ispell-dictionary dict
ispell-local-dictionary dict
ispell-local-dictionary-overridden t)
(ispell-internal-change-dictionary)
(setq ispell-buffer-session-localwords nil)
(run-hooks 'ispell-change-dictionary-hook))))
:preselect (map-elt mu-dictionaries ispell-dictionary)
:buffer "*helm select dictionary*")
(flyspell-buffer))


I merely followed the guidelines on the Helm wiki, but let’s break it down.

(helm :source
(helm-build-sync-source "Dictionaries"


The source of my Helm command comes from the simple alist defined before (mu-dictionaries), so helm-build-sync-source is enough.

:candidates (map-values mu-dictionaries)


Here I am using the excellent map.el to get the clearer “English” and “Italian” labels as :candidates for the Helm buffer.

:action (lambda (candidate)
(let ((dict (car (rassoc candidate mu-dictionaries))))
(ispell-set-spellchecker-params)
(setq ispell-dictionary dict
ispell-local-dictionary dict
ispell-local-dictionary-overridden t)
(ispell-internal-change-dictionary)
(setq ispell-buffer-session-localwords nil)
(run-hooks 'ispell-change-dictionary-hook)))


The anonymous function for :action is a small version of ispell-change-dictionary, containing only what I need for this operation to complete successfully. Note the use of rassoc: this is how I get the key corresponding to the value of candidate in mu-dictionaries. I didn’t find a similar facility in map.el, but rassoc and car get the job done.

:preselect (map-elt mu-dictionaries ispell-dictionary)


Here I am using the current dictionary as the preselected element in the Helm buffer. I could use :input to have it offered on the prompt, but I prefer to have the prompt empty in this case.

If you look closely at ispell-change-dictionary source code, you can understand the triviality of mu-select-dictionary: ispell-change-dictionary uses completing-read, so in my case it already pops up a Helm buffer. But I am still happy with this solution, and not just because I prefer “Dictionaries” instead of “ispell-change-dictionary” as the source name displayed in my Helm buffer. (Yes, of course it’s mostly because of it.)

As a bonus, thanks to flyspell-buffer the current buffer is spell-checked right after the new dictionary is selected.