#+title: jinx.el - Enchanted Spell Checker
#+author: Daniel Mendler
#+language: en
#+export_file_name: jinx.texi
#+texinfo_dir_category: Emacs misc features
#+texinfo_dir_title: Jinx: (jinx).
#+texinfo_dir_desc: Enchanted Spell Checker

#+html: <a href="https://www.gnu.org/software/emacs/"><img alt="GNU Emacs" src="https://github.com/minad/corfu/blob/screenshots/emacs.svg?raw=true"/></a>
#+html: <a href="https://elpa.gnu.org/packages/jinx.html"><img alt="GNU ELPA" src="https://elpa.gnu.org/packages/jinx.svg"/></a>
#+html: <a href="https://elpa.gnu.org/devel/jinx.html"><img alt="GNU-devel ELPA" src="https://elpa.gnu.org/devel/jinx.svg"/></a>
#+html: <a href="https://releases.melpa.org/#/jinx"><img alt="MELPA Releases" src="https://releases.melpa.org/packages/jinx-badge.svg"/></a>
#+html: <a href="https://snapshots.melpa.org/#/jinx"><img alt="MELPA Snapshots" src="https://snapshots.melpa.org/packages/jinx-badge.svg"/></a>

Jinx is a fast just-in-time spell-checker for Emacs. Jinx highlights misspelled
words in the text of the visible portion of the buffer. For efficiency, Jinx
highlights misspellings lazily, recognizes window boundaries and text folding,
if any. For example, when unfolding or scrolling, only the newly visible part of
the text is checked if it has not been checked before. Each misspelling can be
corrected from a list of dictionary words presented as a completion menu.

Installing Jinx is straight-forward and configuring should not need much
intervention. Jinx can be used completely on its own, and is designed as a full
replacement for Emacs's built-in spell-checker Ispell, but it can also safely
co-exist with it.

Jinx's high performance and low resource usage comes from directly calling the
API of the [[https://rrthomas.github.io/enchant/][Enchant library]]. Jinx automatically compiles =jinx-mod.c= and loads the
dynamic module at startup. By binding directly to the native Enchant API, Jinx
avoids slower inter-process communication. Enchant is used by other text editors
and supports multiple backends like [[https://nuspell.github.io/][Nuspell]], [[https://hunspell.github.io/][Hunspell]] and [[http://aspell.net/][Aspell]], and the system
spell checkers of Mac or Windows.

Jinx supports spell-checking multiple languages in the same buffer. See the
=jinx-languages= variable to customize for multiple languages. Jinx can flexibly
ignore misspellings via faces (=jinx-exclude-faces= and =jinx-include-faces=),
regular expressions (=jinx-exclude-regexps=), and programmable predicates. Jinx
comes preconfigured for the most important Emacs major modes. Modes like
JavaScript, TypeScript, Java, Ruby or Rust are listed in =jinx-camel-modes=. For
these modes composite words in =camelCase= and =PascalCase= are accepted.

#+html: <img src="https://github.com/minad/jinx/blob/screenshots/screenshot.png?raw=true">

* Installation

Jinx can be installed from [[https://elpa.gnu.org/packages/jinx.html][ELPA]] or [[https://releases.melpa.org/#/jinx][MELPA]] directly with =package-install=.

Your Emacs must be compiled with dynamic module support. Furthermore a C
compiler must be available on your system (=gcc= or =clang=). Jinx requires
=libenchant=, which is needed to compile the native module at install time. If
=pkgconf= or =pkg-config= is available, Jinx will use it to locate =libenchant= during
installation. Depending on your BSD or Linux distribution you have to install
different packages:

- Debian, Ubuntu: =libenchant-2-dev=, =pkgconf=
- Arch, Gentoo: =enchant=, =pkgconf=
- Guix: =emacs-jinx= defined in =emacs-xyz.scm=
- Nix: =jinx= defined in =elpa-packages.nix=
- Void: =enchant2-devel=, =pkgconf=
- Fedora: =emacs-jinx= or =enchant2-devel=, =pkgconf-pkg-config=
- OpenSUSE: =emacs-jinx= or =enchant-devel=, =pkgconf-pkg-config=
- FreeBSD, OpenBSD, Mac: =enchant2=, =pkgconf=

On Windows the installation of the native module may require manual
intervention.

* Configuration

Jinx has two modes: the command, =global-jinx-mode= activates globally; and the
command, =jinx-mode=, for activating for specific modes.

#+begin_src emacs-lisp
;; Alternative 1: Enable Jinx globally
(add-hook 'emacs-startup-hook #'global-jinx-mode)

;; Alternative 2: Enable Jinx per mode
(dolist (hook '(text-mode-hook prog-mode-hook conf-mode-hook))
  (add-hook hook #'jinx-mode))
#+end_src

The commands =jinx-correct= and =jinx-languages= are marked as autoloads. Invoking
=jinx-correct= corrects the misspellings. Binding =jinx-correct= to =M-$= takes over
that key from the default assignment to =ispell-word=. Since Jinx is independent
of the Ispell package, =M-$= can be re-used.

#+begin_src emacs-lisp
(keymap-global-set "M-$" #'jinx-correct)
(keymap-global-set "C-M-$" #'jinx-languages)
#+end_src

- =M-$= triggers correction for the misspelled word before point.
- =C-u M-$= or =M-x jinx-correct-all= spell-checks the entire buffer.
- =C-u C-u M-$= or =M-x jinx-correct-word= forces correction of the word at point,
  even if it is not misspelled.

A sample configuration with the popular =use-package= macro is shown here:

#+begin_src emacs-lisp
(use-package jinx
  :hook (emacs-startup . global-jinx-mode)
  :bind (("M-$" . jinx-correct)
         ("C-M-$" . jinx-languages)))
#+end_src

See also the [[https://github.com/minad/jinx/wiki][Jinx Wiki]] for additional configuration tips. The wiki documents
configurations to save misspellings as global abbreviations and support for
Ispell =LocalWords=.

* Correcting misspellings

The easiest way to correct a misspelling is to right click on the word,
underlined with a wavy line. Then a context menu will open where you can select
from suggestions.

If you prefer to use the keyboard, invoke the command =jinx-correct=. The
recommended binding is =M-$=, see the configuration section. In order to check the
entire buffer press the keys =C-u M-$= or use =M-x jinx-correct-all=.

Suggested corrections will be displayed as a completion menu. You can press the
displayed digit keys to quickly select a suggestion. Furthermore the menu offers
options to save the word temporarily for the current session, in the personal
dictionary or in the file-local or directory-local variables. Words can be
removed from these places again via =M-x jinx-remove-word=.

You can enter arbitrary input at the correction prompt in order to make the
correction or to store a modified word in the personal dictionary. For example
if you typed =alotriomorpc=, the prompt offers you the option =@alotriomorpc= which
would add this word to your personal dictionary upon selection. You can then
correct the option to =@allotriomorphic= and add it to the dictionary.

While inside the =jinx-correct= prompt, the keys =M-n= and =M-p= are bound to
=jinx-next= and =jinx-previous= respectively and allow you to move the next and
previous misspelled word.

* Completion configuration

The completion menu is compatible with the popular completion UIs: Vertico, Mct,
Icomplete, Helm and the default completions buffer. In case you use Vertico I
suggest that you tweak the completion display via =vertico-multiform-mode= for the
completion category =jinx=. You can for example use the grid display such that
more suggestions fit on the screen and enable annotations.

#+begin_src emacs-lisp
(add-to-list 'vertico-multiform-categories
             '(jinx grid (vertico-grid-annotate . 20) (vertico-count . 4)))
(vertico-multiform-mode)
#+end_src

* Navigating between misspellings

When correcting a word with =jinx-correct=, the movement commands =jinx-next= and
=jinx-previous= are available on the keys =M-n= and =M-p= to navigate to the next and
previous misspelling respectively. The movement commands work from within the
minibuffer during =jinx-correct= and also globally outside the minibuffer context.

While the commands are not bound globally by default, they are available as =M-n=
and =M-p= if point is placed on top of a misspelled word overlay. If you want you
can add them and other commands to the =jinx-mode-map=, such that they are always
available independent of point placement. If =repeat-mode= from Emacs 28 is
enabled, the movement can be repeated with the keys =n= and =p=.

* Enchant backends and personal dictionaries

Enchant uses different backends for different languages. The backends are
ordered as specified in the personal configuration file
=~/.config/enchant/enchant.ordering= and the system-wide configuration file
=/usr/share/enchant-2/enchant.ordering=. Enchant uses Hunspell as default backend
for most languages. For English Enchant prefers Aspell and for Finnish and
Turkish special backends called Voikko and Zemberek are used.

Depending on the backend the personal dictionary will be taken from different
locations, e.g., =~/.aspell.LANG.pws= or =~/.config/enchant/LANG.dic=. It is
possible to symlink different personal dictionaries such that they are shared by
different spell checkers. See the [[https://rrthomas.github.io/enchant/lib/enchant.html][Enchant manual]] for details.

* Alternatives

There exist multiple alternative spell-checking packages for Emacs, most
famously the builtin ispell.el and flyspell.el packages. The main advantages of
Jinx are its automatic checking of the visible text, its sharp focus on
performance and the ability to easily use multiple dictionaries at once. The
following three alternative packages come closest to the behavior of Jinx.

- [[https://github.com/astoff/jit-spell][jit-spell]]: Jinx borrows ideas from Augusto Stoffel's Jit-spell. Jit-spell uses
  the less efficient Ispell process communication instead of Jinx's calling a
  native API. Since Jit-spell does not restrict spell checking to the visible
  text only, it may enqueue the entire buffer too eagerly for checking. This
  happens when scrolling around or when stealth font locking is enabled. For
  this reason, Jit-spell affected load and latency in my tests ([[https://github.com/astoff/jit-spell/issues/9][issue on Github]]).

- [[https://codeberg.org/ideasman42/emacs-spell-fu][spell-fu]]: The idea to check words just in the visible text came from Campbell
  Barton's spell-fu package. Spell-fu is fast but incurs high memory overhead on
  account of its dictionary in a hash table. For languages with compound words
  and inflected word forms, this overhead magnifies ([[https://codeberg.org/ideasman42/emacs-spell-fu/issues/40][issue on Codeberg]]). By
  accessing the Enchant API directly, Jinx avoids any overhead. Jinx also
  benefits from the advanced spell-checker algorithms of Enchant (affixation,
  compound words, etc.).

- flyspell: Flyspell is a built-in package. Flyspell highlights misspellings
  while typing. Only the word under the cursor is spell-checked. Jinx, on the
  other hand, is more effective because it automatically checks for misspellings
  in the entire visible text of the buffer at once. Flyspell can check the
  entire buffer but must be instructed to do so via the command =flyspell-buffer=.
