diff options
| author | Reuben Thomas | 2016-12-04 22:39:27 +0000 |
|---|---|---|
| committer | Reuben Thomas | 2017-08-20 11:55:40 +0100 |
| commit | 60d417545a2852d36427799691792e4ddff8f86c (patch) | |
| tree | a41da04223d1129ee7862819f985db54c641e5bb | |
| parent | dbd3a17cb068148bd49e288eb0b44ca7eb4a4e3c (diff) | |
| download | emacs-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.el | 92 |
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. |
| 610 | If setting the encoding is supported by spellchecker and is selectable from | 616 | 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." | |||
| 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. | ||
| 1205 | Internal 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. | ||
| 1217 | If 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. |
| 1746 | This allows improving the suggestion list based on actual misspellings." | 1809 | This allows improving the suggestion list based on actual misspellings. |
| 1747 | (and ispell-really-aspell | 1810 | Only 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 | ||