Effective spell check in Emacs

  |   Source

CREATED: <2014-01-21>

UPDATED: <2016-12-07 Wed>

I use Flyspell in web-mode. But the solution works in other major modes.

I assume you already turn on flyspell for all the programming languages by inserting below code into .emacs,

(add-hook 'prog-mode-hook 'flyspell-prog-mode)

There is also a short tutorial on:

  • How to setup flyspell
  • Difference between hunspell and aspell
  • How to setup hunspell

1 Spell check in HTML

I will check,

  • Text between html tag like <label>Please input email</label>
  • Value of html input control like <input type"text" value="Please input your name">=
  • CSS class name like <div class"btn btn-default" />=

My setup:

;; {{ flyspell setup for web-mode
(defun web-mode-flyspell-verify ()
  (let* ((f (get-text-property (- (point) 1) 'face))
         rlt)
    (cond
     ;; Check the words with these font faces, possibly.
     ;; this *blacklist* will be tweaked in next condition
     ((not (memq f '(web-mode-html-attr-value-face
                     web-mode-html-tag-face
                     web-mode-html-attr-name-face
                     web-mode-constant-face
                     web-mode-doctype-face
                     web-mode-keyword-face
                     web-mode-comment-face ;; focus on get html label right
                     web-mode-function-name-face
                     web-mode-variable-name-face
                     web-mode-css-property-name-face
                     web-mode-css-selector-face
                     web-mode-css-color-face
                     web-mode-type-face
                     web-mode-block-control-face)))
      (setq rlt t))
     ;; check attribute value under certain conditions
     ((memq f '(web-mode-html-attr-value-face))
      (save-excursion
        (search-backward-regexp "=['\"]" (line-beginning-position) t)
        (backward-char)
        (setq rlt (string-match "^\\(value\\|class\\|ng[A-Za-z0-9-]*\\)$"
                                (thing-at-point 'symbol)))))
     ;; finalize the blacklist
     (t
      (setq rlt nil)))
    rlt))
(put 'web-mode 'flyspell-mode-predicate 'web-mode-flyspell-verify)
;; }}

I use web-mode for HTML files. The technique applies on other modes (php-mode, html-mode ….) .

2 Don't display doublon (double word) as error

Modern CSS frameworks like Bootstrap make doublon unavoidable. For example, CSS class name btn btn-default contains double word btn.

So we need stop displaying doublon as error in HTML,

(defvar flyspell-check-doublon t
  "Check doublon (double word) when calling `flyspell-highlight-incorrect-region'.")
 (make-variable-buffer-local 'flyspell-check-doublon)

(defadvice flyspell-highlight-incorrect-region (around flyspell-highlight-incorrect-region-hack activate)
  (if (or flyspell-check-doublon (not (eq 'doublon (ad-get-arg 2))))
      ad-do-it))

(defun web-mode-hook-setup ()
  (flyspell-mode 1)
  (setq flyspell-check-doublon nil))

(add-hook 'web-mode-hook 'web-mode-hook-setup)

3 Spell check camel case strings

We can check camel cased string/variable/function if and only if aspell is used.

Insert below code into .emacs,

;; if (aspell installed) { use aspell}
;; else if (hunspell installed) { use hunspell }
;; whatever spell checker I use, I always use English dictionary
;; I prefer use aspell because:
;; 1. aspell is older
;; 2. looks Kevin Atkinson still get some road map for aspell:
;; @see http://lists.gnu.org/archive/html/aspell-announce/2011-09/msg00000.html
(setq ispell-program-name "aspell"
      ;; force the English dictionary, support Camel Case spelling check (tested with aspell 0.6)
      ispell-extra-args '("--sug-mode=ultra" "--lang=en_US" "--run-together" "--run-together-limit=5" "--run-together-min=2"))

Please note the option --run-together-min=2 means the minimum length of each sub-word extracted from camel case string is two. Please run man aspell in shell for details.

4 Summary

EmacsWiki suggests (flyspell-prog-mode) which only checks typo in comments.

But as I proved, Emacs gives you full freedom to design a different solution.

Screen shot (typos are underscored):

spell-check-html-in-emacs.png

5 Javascript and ReactJS setup (OPTIONAL)

If you fully understand my previous sections, you don't need read this section.

Insert below code into .emacs,

(defun js-flyspell-verify ()
  (let* ((f (get-text-property (- (point) 1) 'face)))
    ;; *whitelist*
    ;; only words with following font face will be checked
    (memq f '(js2-function-call
              js2-function-param
              js2-object-property
              font-lock-variable-name-face
              font-lock-string-face
              font-lock-function-name-face))))
(put 'js2-mode 'flyspell-mode-predicate 'js-flyspell-verify)
(put 'rjsx-mode 'flyspell-mode-predicate 'js-flyspell-verify)
Comments powered by Disqus