Subject: bug#52743: 29.0.50; Ispell completion-at-point function

From: Eric Abrahamsen <[email-masked]>
Date: Wed, 22 Dec 2021 15:24:55 -0800

Ispell doesn't currently come with a function suitable for use in
`completion-at-point-functions' -- there's a FIXME comment to that
effect on `ispell-complete-word'.

I really don't think that `flyspell-mode' should be clobbering C-M-i by
default, and replacing it with `flyspell-auto-correct-word': this
essentially bypasses the entire capf machinery, and means you can't
combine dictionary-based completion with any other completion.

I've taken a stab at writing an `ispell-completion-at-point' function,
to be added to `completion-at-point-functions'. Perhaps
`flyspell-use-meta-tab' could be extended to accept the symbol 'capf, in
which case `ispell-completion-at-point' would be added to
`c-a-p-functions', and M-TAB/C-M-i would be left alone.

The function currently doesn't work very well. I don't know what the
`ispell-filter' is, or why it raises an error with "Ispell and its
process have different character maps", but I'm sure I can figure that
out. I wanted to raise this here first, to get some feedback.

I know capf prefers its functions to simply return a table, but that
seems unwieldy when we're talking about a completion dictionary with
potentially hundreds of thousands of entries. The function below tries
two things:

- ispell-lookup-words, which uses the ispell process against
  `ispell-complete-word-dict' and `ispell-alternate-dictionary' to find
  completions.
- ispell--run-on-word, which does a full spell-check on the word, and
  returns a list of potential corrections. This function and its return
  value are more complicated than I fully understand now, but I can
  spend some time figuring them out.

So both options involve invoking the ispell process, and the function
does the filtering itself. I know this isn't optimal, and I could also
do a version that reads ispell-complete-word-dict or
ispell-alternate-dictionary and returns the whole contents, with
caching.

(defun ispell-completion-at-point ()
  (condition-case nil
      (progn
	(ispell-set-spellchecker-params)
	(ispell-accept-buffer-local-defs)
	(pcase-let* ((`(,word ,start ,end) (ispell-get-word nil))
		     (table
		      (or (ignore-errors (ispell-lookup-words word))
			  (let ((run-return (ispell--run-on-word word)))
			    (when (and (listp run-return)
				       (> (length run-return) 0))
			      run-return)))))
	  (list start end table)))
    ;; `user-error' is signaled when ispell fails to find a word to
    ;; work on, in which case we want to silently return nil.
    (user-error nil)))

Anyway, please comment!

Eric