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.