When flyspell is enabled it doesn't show spelling errors on all visible text (like most text editors that support spell checking).

When moving the cursor over text, misspelled words become underlined.

While running flyspell-buffer works, it's a manual operation and can be slow on large files.

Is there a way to run the spell checker on visible text, updating after scrolling? Perhaps using delays so it doesn't make scrolling lag.

  • I don't like the window-scroll-functions hook because there are exceptions, which took me quite a bit of time to finally semi-understand [after pestering Eli and Stefan with a few bug reports ...]; and, I combined it with the post-command-hook to try and come up with something for all situations .... Eventually, I took the plunge and started learning C and am now pestering Eli and Stefan on the Emacs devel mailing list instead ... :) To see how this can work in Lisp, have a look at the linum.el library that has been deprecated in favor of built-in line number in Emacs 26+. – lawlist May 24 '19 at 0:19
  • It seems the existing hooks aren't well suited to performing this operation, especially since launching a process from a timer is a no-no (gives many Blocking call to accept-process-output with quit inhibited!! messages). – ideasman42 May 24 '19 at 3:00
  • Why not just turn on the flyspell-mode? – scribe Jun 21 '20 at 22:13
  • @scribe because it doesn't show errors of on-screen text, only text you type or move your cursor over. The only alternative is to run it on the entire buffer which can be slow (more than 5+ seconds on larger files). – ideasman42 Jun 22 '20 at 1:12

For reference: The example below runs the spell checker on a timer, however it gives many messages:

Blocking call to accept-process-output with quit inhibited!!

.. Which I worked around by temporarily disabling logging, however this is far from ideal.

On the other hand, in practice I've found this to be usable.

(defvar-local flyspell-visible-last-window-range nil)

(defun flyspell-visible--overlay-refresh-from-timer ()
  (when (equal flyspell-visible--delay-buffer (current-buffer))
        (sta (window-start))
        (end (window-end))
        (range (list sta end))
      (when (not (equal range flyspell-visible-last-window-range))
        (setq inhibit-redisplay t)

          ((log-max message-log-max))
          ;; don't log
          (setq message-log-max nil)
          (flyspell-region sta end)
          (setq message-log-max log-max)
          (message nil)
        (setq-local flyspell-visible-last-window-range range)
        (setq inhibit-redisplay nil)

;; Timer
(defvar flyspell-visible--delay-timer nil)
(defvar flyspell-visible--delay-buffer nil)

(defun flyspell-visible-turn-on ()
  "Enable spell checking on a timer"
  (when (timerp flyspell-visible--delay-timer)
    (cancel-timer flyspell-visible--delay-timer))
  ;; Delay for spelling - should be ok, only runs when scroll changes.
  (setq flyspell-visible--delay-buffer (current-buffer))
  (setq flyspell-visible--delay-timer
    (run-with-idle-timer 1.0 t 'flyspell-visible--overlay-refresh-from-timer))

(defun flyspell-visible-turn-off ()
  (when (timerp flyspell-visible--delay-timer)
    (cancel-timer flyspell-visible--delay-timer))
  (setq flyspell-visible--delay-timer nil)
  (setq flyspell-visible--delay-buffer nil)
  (setq flyspell-visible-last-window-range nil)

  • 1
    Instead of a pair of setqs, just do (let ((message-log-max nil)) (flyspell-region sta end) (message nil)) – npostavs May 26 '19 at 13:59

There are minor modes that do just this, although they don't use flyspell.


Example use:

(use-package spell-fu)


Example use:

(use-package wcheck-mode
  :commands (wcheck-mode)
    '(wcheck-default-face ((t (:underline (:color: "red" :style wave)))))

  (setq wcheck-language-data
      ("American English"
        (program . "/usr/bin/enchant-2")
        (args "-l" "-d" "en_US")
        (action-program . "/usr/bin/enchant-2")
        (action-args "-a" "-d" "en_US")
        (action-parser . enchant-suggestions-menu)
          ;; Only check comments & strings.
          ((emacs-lisp-mode c-mode)
            read font-lock-comment-face
            read font-lock-string-face
  (setq wcheck-language "American English")


Example use:

(load "speck")
(setq-default speck-syntactic t)  ;; Only check comments and strings.
(setq-default speck-engine 'Aspell)
(setq-default speck-delay 0.1)
(setq-default speck-pause 0.1)

  '(speck-guess ((t (:underline (:color: "red" :style wave)))))
  '(speck-miss ((t (:underline (:color: "orange" :style wave)))))
  '(speck-doublet ((t (:underline (:color: "brown" :style wave)))))
  '(speck-mouse ((t (:underline (:color: "yellow" :style wave)))))
  '(speck-query ((t (:underline (:color: "or" :style wave)))))
(setq speck-personal-dictionary-file ispell-personal-dictionary)

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.