18

From the moment when I started using Flyspell, it always was painful to use. I rarely check entire buffer for misspelled words, usually I type and once I see underlined word I want to correct it instantly and continue typing. This is simply because I fear to forget to correct the word later and because when I start comprehensive checking of buffer, iSpell gives too many false positives. So, almost always I correct words as soon as possible while I type.

Here is built-in functions I know about and reasons why they are not efficient (for me):

  • flyspell-correct-word — This is bound to mouse click. Forget it, I'm not going to use mouse.

  • flyspell-correct-word-before-point — I used it for some time. It has two downsides:

    1. By default it shows GUI menu that is difficult to operate without mouse and it's really cumbersome when you have list of corrections with more than 10 items.

    2. It doesn't work for word long before point. I type fast and when I spot misspelled word I typically have one or two words between the misspelled word and point. I need to move point, correct word, return back. Oh.

  • flyspell-auto-correct-word — See point 2 ↑ plus it doesn't work well when you have long list of completions (because of cycling).

Q: How can I use Flyspell efficiently, with keyboard, being able to choose from list of completions without cycling, and with ability to correct even more or less distant words?

Acceptable answers include packages that help to navigate to previous misspelled word, correct it and get back or something like that as well as little snippets, possibly from your own configuration file, since I believe other people figured out their ways to efficiently interact with Flyspell.

14

The inbuilt flyspell-auto-correct-previous-word works like a charm for me. I have bound it to F12 and I haven't looked back. It has consistently correctly words for me not matter how many words back the incorrect word was.

From the function documentation:

flyspell-auto-correct-previous-word is an interactive compiled Lisp
function in `flyspell.el'.

(flyspell-auto-correct-previous-word POSITION)

For more information check the manuals.

Auto correct the first misspelled word that occurs before point.
But don't look beyond what's visible on the screen.

In addition, if the first time autocorrect did not give you the right word, keep on hitting your bound key (F12 in my case) to cycle through all options.

[My spell check config]

  • 1
    I see you still need to cycle though all possible corrections. Try it on ttwo for example, though my menu I see 26 proposed corrections! – Mark Karpov Aug 20 '15 at 20:07
  • 3
    ttwo auto-corrected to two in the first try. But I know what you mean. From practical experience, in about 90% of the cases, the first try gets the right word, probably the remaining 9% requires 2-3 tries and that last 1% is when flyspell just doesn't figure it out and I need to go through 5+ tries (or I manually fix it). – Kaushal Modi Aug 20 '15 at 20:14
  • I see, your method has its own benefits (simplicity). Maybe I'll switch to it after all. – Mark Karpov Aug 20 '15 at 20:17
  • 2
    Very nice -- this is what I expected to find, and missed it because I was binding the default key to something else. This is bound to C-; by default, and you can customize flyspell-auto-correct-binding to bind it to another key in the flyspell map. (Or just bind it the normal way, of course.) – glucas Aug 20 '15 at 20:29
6

I'm pretty sure other people will come up with different solutions that will be useful for future readers. However, here is how I currently handle this.

I think flyspell-correct-word-before-point is a good place to start, because it at least can be invoked with key pressing and it displays menu of possible corrections. To fix the menu I've written package Ace Popup Menu that uses Avy as backend. This replaces GUI popup menu that flyspell-correct-word-before-point uses (the function is called x-popup-menu) with textual popup menu that presents labeled menu items: one or two key strokes and you're done.

To solve second problem (inability to correct words “at distance”) I've written this helper:

(defun flyspell-correct-previous (&optional words)
  "Correct word before point, reach distant words.

WORDS words at maximum are traversed backward until misspelled
word is found.  If it's not found, give up.  If argument WORDS is
not specified, traverse 12 words by default.

Return T if misspelled word is found and NIL otherwise.  Never
move point."
  (interactive "P")
  (let* ((Δ (- (point-max) (point)))
         (counter (string-to-number (or words "12")))
         (result
          (catch 'result
            (while (>= counter 0)
              (when (cl-some #'flyspell-overlay-p
                             (overlays-at (point)))
                (flyspell-correct-word-before-point)
                (throw 'result t))
              (backward-word 1)
              (setq counter (1- counter))
              nil))))
    (goto-char (- (point-max) Δ))
    result))

This seems to work.

5

With helm-flyspell you can choose from the list of corrections efficiently. I use the following code to jump to the errors and correct them with it, it saves the position of the point to the mark-ring so you can jump back to the position where you started or corrected words previously:

(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))))))


(defun check-previous-spelling-error ()
  "Jump to previous spelling error and correct it"
  (interactive)
  (push-mark-no-activate)
  (flyspell-goto-previous-error 1)
  (call-interactively 'helm-flyspell-correct))

(defun check-next-spelling-error ()
  "Jump to next spelling error and correct it"
  (interactive)
  (push-mark-no-activate)
  (flyspell-goto-next-error)
  (call-interactively 'helm-flyspell-correct))

(defun push-mark-no-activate ()
  "Pushes `point' to `mark-ring' and does not activate the region
 Equivalent to \\[set-mark-command] when \\[transient-mark-mode] is disabled"
  (interactive)
  (push-mark (point) t nil)
  (message "Pushed mark to ring"))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.