checkers/spell: add +flyspell flag

spell-fu lacks support for multiple dictionaries, affix expansion, and
many non-English dictionaries, so I've added back flyspell support to
the spell module, but opt-in, because it is still the significantly
slower option and spell-fu may support them one day.

If not, tlikonen/wcheck-mode is another alternative to consider.

Fixes #3813
This commit is contained in:
Henrik Lissner 2020-08-23 18:48:50 -04:00
parent 97471f11db
commit e3750dbf66
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
8 changed files with 313 additions and 111 deletions

View file

@ -9,17 +9,23 @@
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#prerequisites][Prerequisites]]
- [[#aspell][Aspell]]
- [[#hunspell][Hunspell]]
- [[#features][Features]]
- [[#configuration][Configuration]]
- [[#changing-how-quickly-spell-fu-spellchecks-after-changes][Changing how quickly spell-fu spellchecks after changes]]
- [[#spell-fu-users][Spell-fu users]]
- [[#flyspell-users][Flyspell users]]
- [[#reducing-false-positives-by-disabling-spelling-on-certain-faces][Reducing false positives by disabling spelling on certain faces]]
- [[#spell-fu-users-1][Spell-fu users]]
- [[#flyspell-users-1][Flyspell users]]
- [[#adding-or-removing-words-to-your-personal-dictionary][Adding or removing words to your personal dictionary]]
- [[#troubleshooting][Troubleshooting]]
* Description
This modules provides spellchecking powered by =aspell= or =hunspell=.
Spellcheck is automatically loaded in all ~text-mode~ derivatives, which
Spellcheck is automatically loaded in many ~text-mode~ derivatives, which
includes ~org-mode~, ~markdown-mode~, the Git Commit buffer (from magit),
~mu4e-compose-mode~, and others.
@ -27,63 +33,137 @@ includes ~org-mode~, ~markdown-mode~, the Git Commit buffer (from magit),
This module has no dedicated maintainers.
** Module Flags
+ =+flyspell= Use =flyspell= instead of =spell-fu=. It's significantly slower,
but supports multiple languages and dictionaries.
+ =+aspell= Use =aspell= as a backend for correcting words.
+ =+hunspell= Use =hunspell= as a backend for correcting words.
+ =+everywhere= Use spellcheck in prog-mode and conf-mode derivatives as well as
text-mode. Basically, enable =spell-fu-mode= everywhere.
+ =+everywhere= Spell check in programming modes as well (in comments).
** Plugins
+ [[https://gitlab.com/ideasman42/emacs-spell-fu][spell-fu]]
+ if =+flyspell=
+ [[https://github.com/d12frosted/flyspell-correct][flyspell-correct]]
+ [[https://github.com/d12frosted/flyspell-correct#flyspell-correct-ivy-interface][flyspell-correct-ivy]] (=completion/ivy=)
+ [[https://github.com/d12frosted/flyspell-correct#flyspell-correct-helm-interface][flyspell-correct-helm]] (=completion/helm=)
+ [[https://github.com/d12frosted/flyspell-correct#flyspell-correct-popup-interface][flyspell-correct-popup]] (if *neither* =completion/ivy= or =completion/helm=)
+ [[https://github.com/rolandwalker/flyspell-lazy][flyspell-lazy]]
+ else
+ [[https://gitlab.com/ideasman42/emacs-spell-fu][spell-fu]]
* Prerequisites
This module requires =aspell= to be installed, whether or not you intend to use
=hunspell= as the word correction backend. =spell-fu= does not yet support
generating the word list with a custom command or =hunspell=, but =hunspell= can
still be used to provide correction suggestions when invoking ~ispell-word~.
This module requires one of =aspell= or =hunspell= installed on your system and
in your ~PATH~. They also need dictionaries for your language(s).
You will need =hunspell= installed (via your OS package manager) to use it as a
correction backend.
#+begin_quote
If you *are not* using =+flyspell=, you will need aspell (and a dictionary)
installed whether or not you have =+hunspell= enabled. This is because
=spell-fu= does not support generating the word list with anything other than
=aspell= yet.
#+end_quote
** Aspell
+ Ubuntu: ~apt-get install aspell aspell-en~
+ Arch Linux: ~pacman -S aspell aspell-en~
+ NixOS:
#+BEGIN_SRC nix
{
environment.systemPackages = with pkgs; [
aspell
aspellDicts.en
aspellDicts.en-computers
aspellDicts.en-science
];
}
#+END_SRC
** TODO Hunspell
* Features
+ Spellchecking based on =aspell=.
+ Spell correction using =aspell= or =hunspell= (through ~M-x ispell-word~).
+ Spell checking and correction using =aspell= or =hunspell=.
+ Ignores source code inside org or markdown files.
+ Lazily spellchecking recent changes only when idle.
+ Choosing suggestions using completion interfaces (=ivy= or =helm=).
When using =+everywhere=, ~spell-fu-mode~ is activated for as many major modes
as possible, and not only ~text-mode~ derivatives. =spell-fu= will only spell
check comments in programming major modes.
When using =+everywhere=, spell checking is performed for as many major modes as
possible, and not only ~text-mode~ derivatives. e.g. in comments for programming
major modes.
* Configuration
Dictionary is set by =ispell-dictionary= variable. Can be changed locally with
the function =ispell-change-dictionary=.
** Changing how quickly spell-fu spellchecks after changes
Adjust ~spell-fu-idle-delay~ to change how long spell-fu waits to spellcheck
after recent changes (its default value as ~0.25~).
*** Spell-fu users
Adjust ~spell-fu-idle-delay~ to change how long Emacs waits to spellcheck after
recent changes.
#+BEGIN_SRC elisp
(after! spell-fu
(setq spell-fu-idle-delay 0.5))
(setq spell-fu-idle-delay 0.5)) ; default is 0.25
#+END_SRC
*** Flyspell users
Lazy spellcheck is provided by =flyspell-lazy= package.
=flyspell-lazy-idle-seconds= sets how many idle seconds until spellchecking
recent changes (default as 1), while =flyspell-lazy-window-idle-seconds= sets
how many seconds until the whole window is spellchecked (default as 3).
#+BEGIN_SRC elisp
(after! flyspell
(setq flyspell-lazy-idle-seconds 2))
#+END_SRC
** Reducing false positives by disabling spelling on certain faces
Exclude what faces to not preform spellchecking on by setting
~spell-fu-faces-exclude~ in a buffer-local hook:
*** Spell-fu users
Users can exclude what faces to preform spellchecking on by adjusting
~+spell-excluded-faces-alist~ in a buffer-local hook:
#+BEGIN_SRC elisp
(setq-hook! 'markdown-mode-hook
spell-fu-faces-exclude '(markdown-code-face
markdown-reference-face
markdown-link-face
markdown-url-face
markdown-markup-face
markdown-html-attr-value-face
markdown-html-attr-name-face
markdown-html-tag-name-face))
(setf (alist-get 'markdown-mode +spell-excluded-faces-alist)
'(markdown-code-face
markdown-reference-face
markdown-link-face
markdown-url-face
markdown-markup-face
markdown-html-attr-value-face
markdown-html-attr-name-face
markdown-html-tag-name-face))
#+END_SRC
*** Flyspell users
Flyspell will run a series of predicate functions to determine if a word should be spell checked. You can add your own with ~set-flyspell-predicate!~:
#+BEGIN_SRC elisp
(set-flyspell-predicate! '(markdown-mode gfm-mode)
#'+markdown-flyspell-word-p)
#+END_SRC
Flyspell predicates take no arguments and must return a boolean to determine if
the word at point should be spell checked. For example:
#+BEGIN_SRC elisp
(defun +markdown-flyspell-word-p ()
"Return t if point is on a word that should be spell checked.
Return nil if on a link url, markup, html, or references."
(let ((faces (doom-enlist (get-text-property (point) 'face))))
(or (and (memq 'font-lock-comment-face faces)
(memq 'markdown-code-face faces))
(not (cl-loop with unsafe-faces = '(markdown-reference-face
markdown-url-face
markdown-markup-face
markdown-comment-face
markdown-html-attr-name-face
markdown-html-attr-value-face
markdown-html-tag-name-face
markdown-code-face)
for face in faces
if (memq face unsafe-faces)
return t)))))
#+END_SRC
** Adding or removing words to your personal dictionary
Use ~M-x spell-fu-word-add~ and ~M-x spell-fu-word-remove~ to whitelist words
that you know are not misspellings.
Spell-fu users can call ~M-x spell-fu-word-add~ and ~M-x spell-fu-word-remove~
to whitelist words that you know are not misspellings. These are on =zq= and
=zw=, respectively, for users with Evil enabled.
Neither ispell nor =+flyspell= support managing personal dictionaries within
Emacs.
* TODO Troubleshooting