Add Enchant support to ispell.el (Bug#17742)

* lisp/textmodes/ispell.el (ispell-program-name): Add “enchant”.
(ispell-really-enchant): Add variable.
(ispell-check-version): If using Enchant, check it’s new enough (at
least 1.6.1).  (Like the ispell check, this is absolute: cannot work
without.)
(ispell-enchant-dictionary-alist): Add variable.
(ispell-find-enchant-dictionaries): Add function, based on
ispell-find-aspell-dictionaries.
(ispell-set-spellchecker-params): Allow dictionary auto-detection for
Enchant, and call ispell-find-enchant-dictionaries to find them.  Use
old ispell name to locale mapping code for Enchant too.
(ispell-send-replacement): Make it work with Enchant.
This commit is contained in:
Reuben Thomas 2016-12-04 22:39:27 +00:00
parent 85512e7521
commit 7136e6723d

View file

@ -208,6 +208,10 @@ Must be greater than 1."
:type 'integer :type 'integer
:group 'ispell) :group 'ispell)
;; XXX Add enchant to this list once enchant >= 1.6.1 is widespread.
;; Before that, adding it is useless, as if it is found, it will just
;; cause an error; and one of the other spelling engines below is
;; almost certainly installed in any case, for enchant to use.
(defcustom ispell-program-name (defcustom ispell-program-name
(or (executable-find "aspell") (or (executable-find "aspell")
(executable-find "ispell") (executable-find "ispell")
@ -605,6 +609,8 @@ english.aff). Aspell and Hunspell don't have this limitation.")
"Non-nil if we can use Aspell extensions.") "Non-nil if we can use Aspell extensions.")
(defvar ispell-really-hunspell nil (defvar ispell-really-hunspell nil
"Non-nil if we can use Hunspell extensions.") "Non-nil if we can use Hunspell extensions.")
(defvar ispell-really-enchant nil
"Non-nil if we can use Enchant extensions.")
(defvar ispell-encoding8-command nil (defvar ispell-encoding8-command nil
"Command line option prefix to select encoding if supported, nil otherwise. "Command line option prefix to select encoding if supported, nil otherwise.
If setting the encoding is supported by spellchecker and is selectable from If setting the encoding is supported by spellchecker and is selectable from
@ -739,17 +745,26 @@ Otherwise returns the library directory name, if that is defined."
(and (search-forward-regexp (and (search-forward-regexp
"(but really Hunspell \\([0-9]+\\.[0-9\\.-]+\\)?)" "(but really Hunspell \\([0-9]+\\.[0-9\\.-]+\\)?)"
nil t) nil t)
(match-string 1)))
(setq ispell-really-enchant
(and (search-forward-regexp
"(but really Enchant \\([0-9]+\\.[0-9\\.-]+\\)?)"
nil t)
(match-string 1))))) (match-string 1)))))
(let* ((aspell8-minver "0.60") (let* ((aspell8-minver "0.60")
(ispell-minver "3.1.12") (ispell-minver "3.1.12")
(hunspell8-minver "1.1.6") (hunspell8-minver "1.1.6")
(enchant-minver "1.6.1")
(minver (cond (minver (cond
((not (version<= ispell-minver ispell-program-version)) ((not (version<= ispell-minver ispell-program-version))
ispell-minver) ispell-minver)
((and ispell-really-aspell ((and ispell-really-aspell
(not (version<= aspell8-minver ispell-really-aspell))) (not (version<= aspell8-minver ispell-really-aspell)))
aspell8-minver)))) aspell8-minver)
((and ispell-really-enchant
(not (version<= enchant-minver ispell-really-enchant)))
enchant-minver))))
(if minver (if minver
(error "%s release %s or greater is required" (error "%s release %s or greater is required"
@ -1183,6 +1198,36 @@ dictionary from that list was found."
(list dict)) (list dict))
ispell-hunspell-dictionary-alist :test #'equal)))) ispell-hunspell-dictionary-alist :test #'equal))))
;; Make ispell.el work better with enchant.
(defvar ispell-enchant-dictionary-alist nil
"An alist of parsed Enchant dicts and associated parameters.
Internal use.")
(defun ispell-find-enchant-dictionaries ()
"Find Enchant's dictionaries, and record in `ispell-enchant-dictionary-alist'."
(let* ((dictionaries
(split-string
(with-temp-buffer
(ispell-call-process
(concat ispell-program-name "-lsmod") nil t nil "-list-dicts")
(buffer-string))
" ([^)]+)\n"))
(found
(mapcar #'(lambda (lang)
`(,lang "[[:alpha:]]" "[^[:alpha:]]" "['.-]" t nil nil utf-8))
dictionaries)))
;; Merge into FOUND any elements from the standard ispell-dictionary-base-alist
;; which have no element in FOUND at all.
(dolist (dict ispell-dictionary-base-alist)
(unless (assoc (car dict) found)
(setq found (nconc found (list dict)))))
(setq ispell-enchant-dictionary-alist found)
;; Add a default entry
(let ((default-dict
'(nil "[[:alpha:]]" "[^[:alpha:]]" "['.-]" t nil nil utf-8)))
(push default-dict ispell-enchant-dictionary-alist))))
;; Set params according to the selected spellchecker ;; Set params according to the selected spellchecker
(defvar ispell-last-program-name nil (defvar ispell-last-program-name nil
@ -1208,7 +1253,7 @@ aspell is used along with Emacs).")
(setq ispell-library-directory (ispell-check-version)) (setq ispell-library-directory (ispell-check-version))
t) t)
(error nil)) (error nil))
ispell-encoding8-command) (or ispell-encoding8-command ispell-really-enchant))
;; auto-detection will only be used if spellchecker is not ;; auto-detection will only be used if spellchecker is not
;; ispell and supports a way to set communication to UTF-8. ;; ispell and supports a way to set communication to UTF-8.
(if ispell-really-aspell (if ispell-really-aspell
@ -1216,11 +1261,14 @@ aspell is used along with Emacs).")
(ispell-find-aspell-dictionaries)) (ispell-find-aspell-dictionaries))
(if ispell-really-hunspell (if ispell-really-hunspell
(or ispell-hunspell-dictionary-alist (or ispell-hunspell-dictionary-alist
(ispell-find-hunspell-dictionaries))))) (ispell-find-hunspell-dictionaries))
(if ispell-really-enchant
(or ispell-enchant-dictionary-alist
(ispell-find-enchant-dictionaries))))))
;; Substitute ispell-dictionary-alist with the list of ;; Substitute ispell-dictionary-alist with the list of
;; dictionaries corresponding to the given spellchecker. ;; dictionaries corresponding to the given spellchecker.
;; If a recent aspell or hunspell, use the list of really ;; With programs that support it, use the list of really
;; installed dictionaries and add to it elements of the original ;; installed dictionaries and add to it elements of the original
;; list that are not present there. Allow distro info. ;; list that are not present there. Allow distro info.
(let ((found-dicts-alist (let ((found-dicts-alist
@ -1229,17 +1277,19 @@ aspell is used along with Emacs).")
ispell-aspell-dictionary-alist ispell-aspell-dictionary-alist
(if ispell-really-hunspell (if ispell-really-hunspell
ispell-hunspell-dictionary-alist)) ispell-hunspell-dictionary-alist))
nil)) (if ispell-really-enchant
ispell-enchant-dictionary-alist
nil)))
(ispell-dictionary-base-alist ispell-dictionary-base-alist) (ispell-dictionary-base-alist ispell-dictionary-base-alist)
ispell-base-dicts-override-alist ; Override only base-dicts-alist ispell-base-dicts-override-alist ; Override only base-dicts-alist
all-dicts-alist) all-dicts-alist)
;; While ispell and aspell (through aliases) use the traditional ;; While ispell and aspell (through aliases) use the traditional
;; dict naming originally expected by ispell.el, hunspell ;; dict naming originally expected by ispell.el, hunspell & Enchant
;; uses locale based names with no alias. We need to map ;; use locale-based names with no alias. We need to map
;; standard names to locale based names to make default dict ;; standard names to locale based names to make default dict
;; definitions available for hunspell. ;; definitions available to these programs.
(if ispell-really-hunspell (if (or ispell-really-hunspell ispell-really-enchant)
(let (tmp-dicts-alist) (let (tmp-dicts-alist)
(dolist (adict ispell-dictionary-base-alist) (dolist (adict ispell-dictionary-base-alist)
(let* ((dict-name (nth 0 adict)) (let* ((dict-name (nth 0 adict))
@ -1264,7 +1314,7 @@ aspell is used along with Emacs).")
(setq ispell-args (setq ispell-args
(nconc ispell-args (list "-d" dict-equiv))) (nconc ispell-args (list "-d" dict-equiv)))
(message (message
"ispell-set-spellchecker-params: Missing Hunspell equiv for \"%s\". Skipping." "ispell-set-spellchecker-params: Missing equivalent for \"%s\". Skipping."
dict-name) dict-name)
(setq skip-dict t))) (setq skip-dict t)))
@ -1306,7 +1356,7 @@ aspell is used along with Emacs).")
(nth 4 adict) ; many-otherchars-p (nth 4 adict) ; many-otherchars-p
(nth 5 adict) ; ispell-args (nth 5 adict) ; ispell-args
(nth 6 adict) ; extended-character-mode (nth 6 adict) ; extended-character-mode
(if ispell-encoding8-command (if (or ispell-encoding8-command ispell-really-enchant)
'utf-8 'utf-8
(nth 7 adict))) (nth 7 adict)))
adict) adict)
@ -1742,9 +1792,10 @@ and pass it the output of the last Ispell invocation."
(erase-buffer))))))) (erase-buffer)))))))
(defun ispell-send-replacement (misspelled replacement) (defun ispell-send-replacement (misspelled replacement)
"Notify Aspell that MISSPELLED should be spelled REPLACEMENT. "Notify spell checker that MISSPELLED should be spelled REPLACEMENT.
This allows improving the suggestion list based on actual misspellings." This allows improving the suggestion list based on actual misspellings.
(and ispell-really-aspell Only works for Aspell and Enchant."
(and (or ispell-really-aspell ispell-really-enchant)
(ispell-send-string (concat "$$ra " misspelled "," replacement "\n")))) (ispell-send-string (concat "$$ra " misspelled "," replacement "\n"))))