aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorReuben Thomas2016-12-04 22:39:27 +0000
committerReuben Thomas2017-08-20 11:55:40 +0100
commit60d417545a2852d36427799691792e4ddff8f86c (patch)
treea41da04223d1129ee7862819f985db54c641e5bb
parentdbd3a17cb068148bd49e288eb0b44ca7eb4a4e3c (diff)
downloademacs-60d417545a2852d36427799691792e4ddff8f86c.tar.gz
emacs-60d417545a2852d36427799691792e4ddff8f86c.zip
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.
-rw-r--r--lisp/textmodes/ispell.el92
1 files changed, 78 insertions, 14 deletions
diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el
index 773023a34a6..e6ca32f20d9 100644
--- a/lisp/textmodes/ispell.el
+++ b/lisp/textmodes/ispell.el
@@ -208,6 +208,10 @@ Must be greater than 1."
208 :type 'integer 208 :type 'integer
209 :group 'ispell) 209 :group 'ispell)
210 210
211;; XXX Add enchant to this list once enchant >= 2.1.0 is widespread.
212;; Before that, adding it is useless, as if it is found, it will just
213;; cause an error; and one of the other spelling engines below is
214;; almost certainly installed in any case, for enchant to use.
211(defcustom ispell-program-name 215(defcustom ispell-program-name
212 (or (executable-find "aspell") 216 (or (executable-find "aspell")
213 (executable-find "ispell") 217 (executable-find "ispell")
@@ -605,6 +609,8 @@ english.aff). Aspell and Hunspell don't have this limitation.")
605 "Non-nil if we can use Aspell extensions.") 609 "Non-nil if we can use Aspell extensions.")
606(defvar ispell-really-hunspell nil 610(defvar ispell-really-hunspell nil
607 "Non-nil if we can use Hunspell extensions.") 611 "Non-nil if we can use Hunspell extensions.")
612(defvar ispell-really-enchant nil
613 "Non-nil if we can use Enchant extensions.")
608(defvar ispell-encoding8-command nil 614(defvar ispell-encoding8-command nil
609 "Command line option prefix to select encoding if supported, nil otherwise. 615 "Command line option prefix to select encoding if supported, nil otherwise.
610If setting the encoding is supported by spellchecker and is selectable from 616If 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."
739 (and (search-forward-regexp 745 (and (search-forward-regexp
740 "(but really Hunspell \\([0-9]+\\.[0-9\\.-]+\\)?)" 746 "(but really Hunspell \\([0-9]+\\.[0-9\\.-]+\\)?)"
741 nil t) 747 nil t)
748 (match-string 1)))
749 (setq ispell-really-enchant
750 (and (search-forward-regexp
751 "(but really Enchant \\([0-9]+\\.[0-9\\.-]+\\)?)"
752 nil t)
742 (match-string 1))))) 753 (match-string 1)))))
743 754
744 (let* ((aspell8-minver "0.60") 755 (let* ((aspell8-minver "0.60")
745 (ispell-minver "3.1.12") 756 (ispell-minver "3.1.12")
746 (hunspell8-minver "1.1.6") 757 (hunspell8-minver "1.1.6")
758 (enchant-minver "2.1.0")
747 (minver (cond 759 (minver (cond
748 ((not (version<= ispell-minver ispell-program-version)) 760 ((not (version<= ispell-minver ispell-program-version))
749 ispell-minver) 761 ispell-minver)
750 ((and ispell-really-aspell 762 ((and ispell-really-aspell
751 (not (version<= aspell8-minver ispell-really-aspell))) 763 (not (version<= aspell8-minver ispell-really-aspell)))
752 aspell8-minver)))) 764 aspell8-minver)
765 ((and ispell-really-enchant
766 (not (version<= enchant-minver ispell-really-enchant)))
767 enchant-minver))))
753 768
754 (if minver 769 (if minver
755 (error "%s release %s or greater is required" 770 (error "%s release %s or greater is required"
@@ -1183,6 +1198,49 @@ dictionary from that list was found."
1183 (list dict)) 1198 (list dict))
1184 ispell-hunspell-dictionary-alist :test #'equal)))) 1199 ispell-hunspell-dictionary-alist :test #'equal))))
1185 1200
1201;; Make ispell.el work better with enchant.
1202
1203(defvar ispell-enchant-dictionary-alist nil
1204 "An alist of parsed Enchant dicts and associated parameters.
1205Internal use.")
1206
1207(defun ispell--call-enchant-lsmod (&rest args)
1208 "Call enchant-lsmod with ARGS and return the output as string."
1209 (with-output-to-string
1210 (with-current-buffer
1211 standard-output
1212 (apply 'ispell-call-process
1213 (concat ispell-program-name "-lsmod") nil t nil args))))
1214
1215(defun ispell--get-extra-word-characters (&optional lang)
1216 "Get the extra word characters for LANG as a character class.
1217If LANG is omitted, get the extra word characters for the default language."
1218 (concat "[" (string-trim-right (apply 'ispell--call-enchant-lsmod
1219 (append '("-word-chars") (if lang `(,lang))))) "]"))
1220
1221(defun ispell-find-enchant-dictionaries ()
1222 "Find Enchant's dictionaries, and record in `ispell-enchant-dictionary-alist'."
1223 (let* ((dictionaries
1224 (split-string
1225 (ispell--call-enchant-lsmod "-list-dicts" (buffer-string)) " ([^)]+)\n"))
1226 (found
1227 (mapcar #'(lambda (lang)
1228 `(,lang "[[:alpha:]]" "[^[:alpha:]]"
1229 ,(ispell--get-extra-word-characters) t nil nil utf-8))
1230 dictionaries)))
1231 ;; Merge into FOUND any elements from the standard ispell-dictionary-base-alist
1232 ;; which have no element in FOUND at all.
1233 (dolist (dict ispell-dictionary-base-alist)
1234 (unless (assoc (car dict) found)
1235 (setq found (nconc found (list dict)))))
1236 (setq ispell-enchant-dictionary-alist found)
1237 ;; Add a default entry
1238 (let ((default-dict
1239 `(nil "[[:alpha:]]" "[^[:alpha:]]"
1240 ,(ispell--get-extra-word-characters)
1241 t nil nil utf-8)))
1242 (push default-dict ispell-enchant-dictionary-alist))))
1243
1186;; Set params according to the selected spellchecker 1244;; Set params according to the selected spellchecker
1187 1245
1188(defvar ispell-last-program-name nil 1246(defvar ispell-last-program-name nil
@@ -1208,7 +1266,7 @@ aspell is used along with Emacs).")
1208 (setq ispell-library-directory (ispell-check-version)) 1266 (setq ispell-library-directory (ispell-check-version))
1209 t) 1267 t)
1210 (error nil)) 1268 (error nil))
1211 ispell-encoding8-command) 1269 (or ispell-encoding8-command ispell-really-enchant))
1212 ;; auto-detection will only be used if spellchecker is not 1270 ;; auto-detection will only be used if spellchecker is not
1213 ;; ispell and supports a way to set communication to UTF-8. 1271 ;; ispell and supports a way to set communication to UTF-8.
1214 (if ispell-really-aspell 1272 (if ispell-really-aspell
@@ -1216,11 +1274,14 @@ aspell is used along with Emacs).")
1216 (ispell-find-aspell-dictionaries)) 1274 (ispell-find-aspell-dictionaries))
1217 (if ispell-really-hunspell 1275 (if ispell-really-hunspell
1218 (or ispell-hunspell-dictionary-alist 1276 (or ispell-hunspell-dictionary-alist
1219 (ispell-find-hunspell-dictionaries))))) 1277 (ispell-find-hunspell-dictionaries))
1278 (if ispell-really-enchant
1279 (or ispell-enchant-dictionary-alist
1280 (ispell-find-enchant-dictionaries))))))
1220 1281
1221 ;; Substitute ispell-dictionary-alist with the list of 1282 ;; Substitute ispell-dictionary-alist with the list of
1222 ;; dictionaries corresponding to the given spellchecker. 1283 ;; dictionaries corresponding to the given spellchecker.
1223 ;; If a recent aspell or hunspell, use the list of really 1284 ;; With programs that support it, use the list of really
1224 ;; installed dictionaries and add to it elements of the original 1285 ;; installed dictionaries and add to it elements of the original
1225 ;; list that are not present there. Allow distro info. 1286 ;; list that are not present there. Allow distro info.
1226 (let ((found-dicts-alist 1287 (let ((found-dicts-alist
@@ -1229,17 +1290,19 @@ aspell is used along with Emacs).")
1229 ispell-aspell-dictionary-alist 1290 ispell-aspell-dictionary-alist
1230 (if ispell-really-hunspell 1291 (if ispell-really-hunspell
1231 ispell-hunspell-dictionary-alist)) 1292 ispell-hunspell-dictionary-alist))
1232 nil)) 1293 (if ispell-really-enchant
1294 ispell-enchant-dictionary-alist
1295 nil)))
1233 (ispell-dictionary-base-alist ispell-dictionary-base-alist) 1296 (ispell-dictionary-base-alist ispell-dictionary-base-alist)
1234 ispell-base-dicts-override-alist ; Override only base-dicts-alist 1297 ispell-base-dicts-override-alist ; Override only base-dicts-alist
1235 all-dicts-alist) 1298 all-dicts-alist)
1236 1299
1237 ;; While ispell and aspell (through aliases) use the traditional 1300 ;; While ispell and aspell (through aliases) use the traditional
1238 ;; dict naming originally expected by ispell.el, hunspell 1301 ;; dict naming originally expected by ispell.el, hunspell & Enchant
1239 ;; uses locale based names with no alias. We need to map 1302 ;; use locale-based names with no alias. We need to map
1240 ;; standard names to locale based names to make default dict 1303 ;; standard names to locale based names to make default dict
1241 ;; definitions available for hunspell. 1304 ;; definitions available to these programs.
1242 (if ispell-really-hunspell 1305 (if (or ispell-really-hunspell ispell-really-enchant)
1243 (let (tmp-dicts-alist) 1306 (let (tmp-dicts-alist)
1244 (dolist (adict ispell-dictionary-base-alist) 1307 (dolist (adict ispell-dictionary-base-alist)
1245 (let* ((dict-name (nth 0 adict)) 1308 (let* ((dict-name (nth 0 adict))
@@ -1264,7 +1327,7 @@ aspell is used along with Emacs).")
1264 (setq ispell-args 1327 (setq ispell-args
1265 (nconc ispell-args (list "-d" dict-equiv))) 1328 (nconc ispell-args (list "-d" dict-equiv)))
1266 (message 1329 (message
1267 "ispell-set-spellchecker-params: Missing Hunspell equiv for \"%s\". Skipping." 1330 "ispell-set-spellchecker-params: Missing equivalent for \"%s\". Skipping."
1268 dict-name) 1331 dict-name)
1269 (setq skip-dict t))) 1332 (setq skip-dict t)))
1270 1333
@@ -1306,7 +1369,7 @@ aspell is used along with Emacs).")
1306 (nth 4 adict) ; many-otherchars-p 1369 (nth 4 adict) ; many-otherchars-p
1307 (nth 5 adict) ; ispell-args 1370 (nth 5 adict) ; ispell-args
1308 (nth 6 adict) ; extended-character-mode 1371 (nth 6 adict) ; extended-character-mode
1309 (if ispell-encoding8-command 1372 (if (or ispell-encoding8-command ispell-really-enchant)
1310 'utf-8 1373 'utf-8
1311 (nth 7 adict))) 1374 (nth 7 adict)))
1312 adict) 1375 adict)
@@ -1742,9 +1805,10 @@ and pass it the output of the last Ispell invocation."
1742 (erase-buffer))))))) 1805 (erase-buffer)))))))
1743 1806
1744(defun ispell-send-replacement (misspelled replacement) 1807(defun ispell-send-replacement (misspelled replacement)
1745 "Notify Aspell that MISSPELLED should be spelled REPLACEMENT. 1808 "Notify spell checker that MISSPELLED should be spelled REPLACEMENT.
1746This allows improving the suggestion list based on actual misspellings." 1809This allows improving the suggestion list based on actual misspellings.
1747 (and ispell-really-aspell 1810Only works for Aspell and Enchant."
1811 (and (or ispell-really-aspell ispell-really-enchant)
1748 (ispell-send-string (concat "$$ra " misspelled "," replacement "\n")))) 1812 (ispell-send-string (concat "$$ra " misspelled "," replacement "\n"))))
1749 1813
1750 1814