Commit | Line | Data |
---|---|---|
e8af40ee | 1 | ;;; flyspell.el --- on-the-fly spell checker |
60371a2e | 2 | |
acaf905b | 3 | ;; Copyright (C) 1998, 2000-2012 Free Software Foundation, Inc. |
60371a2e | 4 | |
2da34788 | 5 | ;; Author: Manuel Serrano <Manuel.Serrano@sophia.inria.fr> |
e6d9ec82 | 6 | ;; Maintainer: FSF |
1d8a80f0 | 7 | ;; Keywords: convenience |
60371a2e | 8 | |
e8af40ee | 9 | ;; This file is part of GNU Emacs. |
60371a2e | 10 | |
1fecc8fe | 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
60371a2e | 12 | ;; it under the terms of the GNU General Public License as published by |
1fecc8fe GM |
13 | ;; the Free Software Foundation, either version 3 of the License, or |
14 | ;; (at your option) any later version. | |
60371a2e RS |
15 | |
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ||
21 | ;; You should have received a copy of the GNU General Public License | |
1fecc8fe | 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
60371a2e | 23 | |
3215afc4 | 24 | ;;; Commentary: |
60371a2e RS |
25 | ;; |
26 | ;; Flyspell is a minor Emacs mode performing on-the-fly spelling | |
1d8a80f0 | 27 | ;; checking. |
3215afc4 | 28 | ;; |
1f44857f | 29 | ;; To enable Flyspell minor mode, type M-x flyspell-mode. |
65a5c06a | 30 | ;; This applies only to the current buffer. |
3215afc4 GM |
31 | ;; |
32 | ;; To enable Flyspell in text representing computer programs, type | |
1f44857f DL |
33 | ;; M-x flyspell-prog-mode. |
34 | ;; In that mode only text inside comments is checked. | |
2fced6f9 | 35 | ;; |
60371a2e RS |
36 | ;; Some user variables control the behavior of flyspell. They are |
37 | ;; those defined under the `User variables' comment. | |
60371a2e RS |
38 | |
39 | ;;; Code: | |
2fced6f9 | 40 | |
60371a2e RS |
41 | (require 'ispell) |
42 | ||
042c6fb7 SM |
43 | ;;*---------------------------------------------------------------------*/ |
44 | ;;* Group ... */ | |
45 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 46 | (defgroup flyspell nil |
25f2ad05 | 47 | "Spell checking on the fly." |
60371a2e RS |
48 | :tag "FlySpell" |
49 | :prefix "flyspell-" | |
a9c6d330 | 50 | :group 'ispell |
3215afc4 | 51 | :group 'processes) |
60371a2e | 52 | |
042c6fb7 SM |
53 | ;;*---------------------------------------------------------------------*/ |
54 | ;;* User configuration ... */ | |
55 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 56 | (defcustom flyspell-highlight-flag t |
042c6fb7 | 57 | "How Flyspell should indicate misspelled words. |
0a67052f | 58 | Non-nil means use highlight, nil means use minibuffer messages." |
60371a2e RS |
59 | :group 'flyspell |
60 | :type 'boolean) | |
61 | ||
0a67052f | 62 | (defcustom flyspell-mark-duplications-flag t |
042c6fb7 | 63 | "Non-nil means Flyspell reports a repeated word as an error. |
08fea928 | 64 | See `flyspell-mark-duplications-exceptions' to add exceptions to this rule. |
ae3defd0 RS |
65 | Detection of repeated words is not implemented in |
66 | \"large\" regions; see `flyspell-large-region'." | |
60371a2e RS |
67 | :group 'flyspell |
68 | :type 'boolean) | |
69 | ||
08fea928 | 70 | (defcustom flyspell-mark-duplications-exceptions |
c11325f7 CY |
71 | '((nil . ("that" "had")) ; Common defaults for English. |
72 | ("\\`francais" . ("nous" "vous"))) | |
08fea928 | 73 | "A list of exceptions for duplicated words. |
c11325f7 CY |
74 | It should be a list of (LANGUAGE . EXCEPTION-LIST). |
75 | ||
76 | LANGUAGE is nil, which means the exceptions apply regardless of | |
77 | the current dictionary, or a regular expression matching the | |
78 | dictionary name (`ispell-local-dictionary' or | |
79 | `ispell-dictionary') for which the exceptions should apply. | |
80 | ||
81 | EXCEPTION-LIST is a list of strings. The checked word is | |
82 | downcased before comparing with these exceptions." | |
08fea928 | 83 | :group 'flyspell |
c11325f7 CY |
84 | :type '(alist :key-type (choice (const :tag "All dictionaries" nil) |
85 | string) | |
86 | :value-type (repeat string)) | |
87 | :version "24.1") | |
08fea928 | 88 | |
3215afc4 | 89 | (defcustom flyspell-sort-corrections nil |
042c6fb7 | 90 | "Non-nil means, sort the corrections alphabetically before popping them." |
60371a2e | 91 | :group 'flyspell |
1f44857f | 92 | :version "21.1" |
60371a2e RS |
93 | :type 'boolean) |
94 | ||
3215afc4 | 95 | (defcustom flyspell-duplicate-distance -1 |
042c6fb7 | 96 | "The maximum distance for finding duplicates of unrecognized words. |
2ed1f669 RS |
97 | This applies to the feature that when a word is not found in the dictionary, |
98 | if the same spelling occurs elsewhere in the buffer, | |
c43aed5a | 99 | Flyspell uses a different face (`flyspell-duplicate') to highlight it. |
2ed1f669 | 100 | This variable specifies how far to search to find such a duplicate. |
65a5c06a | 101 | -1 means no limit (search the whole buffer). |
2ed1f669 | 102 | 0 means do not search for duplicate unrecognized spellings." |
60371a2e | 103 | :group 'flyspell |
1f44857f | 104 | :version "21.1" |
e039c773 RS |
105 | :type '(choice (const :tag "no limit" -1) |
106 | number)) | |
60371a2e RS |
107 | |
108 | (defcustom flyspell-delay 3 | |
042c6fb7 | 109 | "The number of seconds to wait before checking, after a \"delayed\" command." |
60371a2e RS |
110 | :group 'flyspell |
111 | :type 'number) | |
112 | ||
113 | (defcustom flyspell-persistent-highlight t | |
042c6fb7 | 114 | "Non-nil means misspelled words remain highlighted until corrected. |
84770261 RS |
115 | If this variable is nil, only the most recently detected misspelled word |
116 | is highlighted." | |
60371a2e RS |
117 | :group 'flyspell |
118 | :type 'boolean) | |
119 | ||
120 | (defcustom flyspell-highlight-properties t | |
042c6fb7 | 121 | "Non-nil means highlight incorrect words even if a property exists for this word." |
60371a2e RS |
122 | :group 'flyspell |
123 | :type 'boolean) | |
124 | ||
125 | (defcustom flyspell-default-delayed-commands | |
126 | '(self-insert-command | |
127 | delete-backward-char | |
3215afc4 GM |
128 | backward-or-forward-delete-char |
129 | delete-char | |
a8c453e6 RS |
130 | scrollbar-vertical-drag |
131 | backward-delete-char-untabify) | |
0a67052f RS |
132 | "The standard list of delayed commands for Flyspell. |
133 | See `flyspell-delayed-commands'." | |
60371a2e | 134 | :group 'flyspell |
1f44857f | 135 | :version "21.1" |
60371a2e RS |
136 | :type '(repeat (symbol))) |
137 | ||
0a67052f RS |
138 | (defcustom flyspell-delayed-commands nil |
139 | "List of commands that are \"delayed\" for Flyspell mode. | |
65a5c06a RS |
140 | After these commands, Flyspell checking is delayed for a short time, |
141 | whose length is specified by `flyspell-delay'." | |
60371a2e RS |
142 | :group 'flyspell |
143 | :type '(repeat (symbol))) | |
144 | ||
3215afc4 GM |
145 | (defcustom flyspell-default-deplacement-commands |
146 | '(next-line | |
147 | previous-line | |
148 | scroll-up | |
149 | scroll-down) | |
150 | "The standard list of deplacement commands for Flyspell. | |
151 | See `flyspell-deplacement-commands'." | |
152 | :group 'flyspell | |
1f44857f | 153 | :version "21.1" |
3215afc4 GM |
154 | :type '(repeat (symbol))) |
155 | ||
156 | (defcustom flyspell-deplacement-commands nil | |
157 | "List of commands that are \"deplacement\" for Flyspell mode. | |
158 | After these commands, Flyspell checking is performed only if the previous | |
159 | command was not the very same command." | |
160 | :group 'flyspell | |
1f44857f | 161 | :version "21.1" |
3215afc4 GM |
162 | :type '(repeat (symbol))) |
163 | ||
60371a2e | 164 | (defcustom flyspell-issue-welcome-flag t |
042c6fb7 | 165 | "Non-nil means that Flyspell should display a welcome message when started." |
60371a2e RS |
166 | :group 'flyspell |
167 | :type 'boolean) | |
168 | ||
73194d67 | 169 | (defcustom flyspell-issue-message-flag t |
042c6fb7 | 170 | "Non-nil means that Flyspell emits messages when checking words." |
73194d67 PJ |
171 | :group 'flyspell |
172 | :type 'boolean) | |
173 | ||
3215afc4 | 174 | (defcustom flyspell-incorrect-hook nil |
042c6fb7 | 175 | "List of functions to be called when incorrect words are encountered. |
91346f54 RS |
176 | Each function is given three arguments. The first two |
177 | arguments are the beginning and the end of the incorrect region. | |
178 | The third is either the symbol `doublon' or the list | |
ebb7de84 | 179 | of possible corrections as returned by `ispell-parse-output'. |
3215afc4 | 180 | |
91346f54 | 181 | If any of the functions return non-nil, the word is not highlighted as |
3215afc4 GM |
182 | incorrect." |
183 | :group 'flyspell | |
1f44857f | 184 | :version "21.1" |
3215afc4 GM |
185 | :type 'hook) |
186 | ||
487301c2 | 187 | (defcustom flyspell-default-dictionary nil |
3215afc4 | 188 | "A string that is the name of the default dictionary. |
1f44857f | 189 | This is passed to the `ispell-change-dictionary' when flyspell is started. |
487301c2 RS |
190 | If the variable `ispell-local-dictionary' or `ispell-dictionary' is non-nil |
191 | when flyspell is started, the value of that variable is used instead | |
192 | of `flyspell-default-dictionary' to select the default dictionary. | |
193 | Otherwise, if `flyspell-default-dictionary' is nil, it means to use | |
194 | Ispell's ultimate default dictionary." | |
3215afc4 | 195 | :group 'flyspell |
1f44857f | 196 | :version "21.1" |
eb1d71ab | 197 | :type '(choice string (const :tag "Default" nil))) |
3215afc4 GM |
198 | |
199 | (defcustom flyspell-tex-command-regexp | |
200 | "\\(\\(begin\\|end\\)[ \t]*{\\|\\(cite[a-z*]*\\|label\\|ref\\|eqref\\|usepackage\\|documentclass\\)[ \t]*\\(\\[[^]]*\\]\\)?{[^{}]*\\)" | |
201 | "A string that is the regular expression that matches TeX commands." | |
202 | :group 'flyspell | |
1f44857f | 203 | :version "21.1" |
3215afc4 GM |
204 | :type 'string) |
205 | ||
206 | (defcustom flyspell-check-tex-math-command nil | |
da3d757b | 207 | "Non-nil means check even inside TeX math environment. |
be7748e7 KR |
208 | TeX math environments are discovered by `texmathp', implemented |
209 | inside AUCTeX package. That package may be found at | |
210 | URL `http://www.gnu.org/software/auctex/'" | |
60371a2e RS |
211 | :group 'flyspell |
212 | :type 'boolean) | |
213 | ||
3215afc4 GM |
214 | (defcustom flyspell-dictionaries-that-consider-dash-as-word-delimiter |
215 | '("francais" "deutsch8" "norsk") | |
216 | "List of dictionary names that consider `-' as word delimiter." | |
217 | :group 'flyspell | |
1f44857f | 218 | :version "21.1" |
3215afc4 | 219 | :type '(repeat (string))) |
60371a2e | 220 | |
3215afc4 | 221 | (defcustom flyspell-abbrev-p |
a8c453e6 | 222 | nil |
042c6fb7 | 223 | "If non-nil, add correction to abbreviation table." |
60371a2e | 224 | :group 'flyspell |
1f44857f | 225 | :version "21.1" |
60371a2e RS |
226 | :type 'boolean) |
227 | ||
3215afc4 GM |
228 | (defcustom flyspell-use-global-abbrev-table-p |
229 | nil | |
042c6fb7 | 230 | "If non-nil, prefer global abbrev table to local abbrev table." |
3215afc4 | 231 | :group 'flyspell |
1f44857f | 232 | :version "21.1" |
3215afc4 | 233 | :type 'boolean) |
2fced6f9 | 234 | |
3215afc4 | 235 | (defcustom flyspell-mode-line-string " Fly" |
042c6fb7 | 236 | "String displayed on the modeline when flyspell is active. |
3215afc4 GM |
237 | Set this to nil if you don't want a modeline indicator." |
238 | :group 'flyspell | |
5c2012be | 239 | :type '(choice string (const :tag "None" nil))) |
3215afc4 GM |
240 | |
241 | (defcustom flyspell-large-region 1000 | |
042c6fb7 | 242 | "The threshold that determines if a region is small. |
fc2cae59 SE |
243 | If the region is smaller than this number of characters, |
244 | `flyspell-region' checks the words sequentially using regular | |
245 | flyspell methods. Else, if the region is large, a new Ispell process is | |
a8c453e6 RS |
246 | spawned for speed. |
247 | ||
ae3defd0 RS |
248 | Doubled words are not detected in a large region, because Ispell |
249 | does not check for them. | |
250 | ||
c2e161b2 | 251 | If this variable is nil, all regions are treated as small." |
3215afc4 | 252 | :group 'flyspell |
1f44857f | 253 | :version "21.1" |
ae3defd0 | 254 | :type '(choice number (const :tag "All small" nil))) |
3215afc4 | 255 | |
73194d67 | 256 | (defcustom flyspell-insert-function (function insert) |
042c6fb7 | 257 | "Function for inserting word by flyspell upon correction." |
73194d67 PJ |
258 | :group 'flyspell |
259 | :type 'function) | |
260 | ||
261 | (defcustom flyspell-before-incorrect-word-string nil | |
262 | "String used to indicate an incorrect word starting." | |
263 | :group 'flyspell | |
264 | :type '(choice string (const nil))) | |
265 | ||
266 | (defcustom flyspell-after-incorrect-word-string nil | |
267 | "String used to indicate an incorrect word ending." | |
268 | :group 'flyspell | |
269 | :type '(choice string (const nil))) | |
270 | ||
cb92c150 GM |
271 | (defvar flyspell-mode-map) |
272 | ||
a8c453e6 | 273 | (defcustom flyspell-use-meta-tab t |
042c6fb7 | 274 | "Non-nil means that flyspell uses M-TAB to correct word." |
a8c453e6 | 275 | :group 'flyspell |
cb92c150 GM |
276 | :type 'boolean |
277 | :initialize 'custom-initialize-default | |
278 | :set (lambda (sym val) | |
279 | (define-key flyspell-mode-map "\M-\t" | |
280 | (if (set sym val) | |
281 | 'flyspell-auto-correct-word)))) | |
a8c453e6 RS |
282 | |
283 | (defcustom flyspell-auto-correct-binding | |
7ad04640 | 284 | [(control ?\;)] |
a8c453e6 RS |
285 | "The key binding for flyspell auto correction." |
286 | :group 'flyspell) | |
287 | ||
042c6fb7 SM |
288 | ;;*---------------------------------------------------------------------*/ |
289 | ;;* Mode specific options */ | |
290 | ;;* ------------------------------------------------------------- */ | |
291 | ;;* Mode specific options enable users to disable flyspell on */ | |
292 | ;;* certain word depending of the emacs mode. For instance, when */ | |
293 | ;;* using flyspell with mail-mode add the following expression */ | |
294 | ;;* in your .emacs file: */ | |
295 | ;;* (add-hook 'mail-mode */ | |
4f91a816 | 296 | ;;* (lambda () (setq flyspell-generic-check-word-predicate */ |
5c9c3eba | 297 | ;;* 'mail-mode-flyspell-verify))) */ |
042c6fb7 | 298 | ;;*---------------------------------------------------------------------*/ |
5c9c3eba | 299 | (defvar flyspell-generic-check-word-predicate nil |
60371a2e | 300 | "Function providing per-mode customization over which words are flyspelled. |
0a67052f RS |
301 | Returns t to continue checking, nil otherwise. |
302 | Flyspell mode sets this variable to whatever is the `flyspell-mode-predicate' | |
303 | property of the major mode name.") | |
5c9c3eba RW |
304 | (make-variable-buffer-local 'flyspell-generic-check-word-predicate) |
305 | (defvaralias 'flyspell-generic-check-word-p | |
306 | 'flyspell-generic-check-word-predicate) | |
60371a2e | 307 | |
042c6fb7 | 308 | ;;*--- mail mode -------------------------------------------------------*/ |
0a67052f RS |
309 | (put 'mail-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify) |
310 | (put 'message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify) | |
fc5e09b3 | 311 | (defvar message-signature-separator) |
60371a2e | 312 | (defun mail-mode-flyspell-verify () |
5c9c3eba | 313 | "Function used for `flyspell-generic-check-word-predicate' in Mail mode." |
a8c453e6 RS |
314 | (let ((header-end (save-excursion |
315 | (goto-char (point-min)) | |
316 | (re-search-forward | |
317 | (concat "^" | |
318 | (regexp-quote mail-header-separator) | |
319 | "$") | |
320 | nil t) | |
321 | (point))) | |
747d0c44 SM |
322 | (signature-begin |
323 | (if (not (boundp 'message-signature-separator)) | |
324 | (point-max) | |
325 | (save-excursion | |
326 | (goto-char (point-max)) | |
327 | (re-search-backward message-signature-separator nil t) | |
328 | (point))))) | |
a8c453e6 | 329 | (cond ((< (point) header-end) |
25c99c6d GM |
330 | (and (save-excursion (beginning-of-line) |
331 | (looking-at "^Subject:")) | |
332 | (> (point) (match-end 0)))) | |
a8c453e6 | 333 | ((> (point) signature-begin) |
53287eb0 GM |
334 | nil) |
335 | (t | |
336 | (save-excursion | |
337 | (beginning-of-line) | |
676019c5 | 338 | (not (looking-at "[>}|]\\|To:"))))))) |
60371a2e | 339 | |
042c6fb7 | 340 | ;;*--- texinfo mode ----------------------------------------------------*/ |
0a67052f | 341 | (put 'texinfo-mode 'flyspell-mode-predicate 'texinfo-mode-flyspell-verify) |
60371a2e | 342 | (defun texinfo-mode-flyspell-verify () |
5c9c3eba | 343 | "Function used for `flyspell-generic-check-word-predicate' in Texinfo mode." |
60371a2e RS |
344 | (save-excursion |
345 | (forward-word -1) | |
346 | (not (looking-at "@")))) | |
347 | ||
042c6fb7 | 348 | ;;*--- tex mode --------------------------------------------------------*/ |
3215afc4 GM |
349 | (put 'tex-mode 'flyspell-mode-predicate 'tex-mode-flyspell-verify) |
350 | (defun tex-mode-flyspell-verify () | |
5c9c3eba | 351 | "Function used for `flyspell-generic-check-word-predicate' in LaTeX mode." |
3215afc4 GM |
352 | (and |
353 | (not (save-excursion | |
9c841316 | 354 | (re-search-backward "^[ \t]*%%%[ \t]+Local" nil t))) |
3215afc4 | 355 | (not (save-excursion |
873f4645 | 356 | (let ((this (point))) |
3215afc4 | 357 | (beginning-of-line) |
873f4645 CY |
358 | (and (re-search-forward "\\\\\\(cite\\|label\\|ref\\){[^}]*}" |
359 | (line-end-position) t) | |
360 | (>= this (match-beginning 0)) | |
361 | (<= this (match-end 0)))))))) | |
3215afc4 | 362 | |
042c6fb7 | 363 | ;;*--- sgml mode -------------------------------------------------------*/ |
3215afc4 GM |
364 | (put 'sgml-mode 'flyspell-mode-predicate 'sgml-mode-flyspell-verify) |
365 | (put 'html-mode 'flyspell-mode-predicate 'sgml-mode-flyspell-verify) | |
23e33070 | 366 | (put 'nxml-mode 'flyspell-mode-predicate 'sgml-mode-flyspell-verify) |
3215afc4 | 367 | |
4c3a215a | 368 | (autoload 'sgml-lexical-context "sgml-mode") |
7e705a1d | 369 | |
3215afc4 | 370 | (defun sgml-mode-flyspell-verify () |
e3c39c01 KR |
371 | "Function used for `flyspell-generic-check-word-predicate' in SGML mode. |
372 | Tag and attribute names are not spell checked, everything else is. | |
373 | ||
374 | String values of attributes are checked because they can be text | |
375 | like <img alt=\"Some thing.\">." | |
376 | ||
377 | (not (memq (car (sgml-lexical-context)) | |
378 | '(tag pi)))) | |
3215afc4 | 379 | |
042c6fb7 SM |
380 | ;;*---------------------------------------------------------------------*/ |
381 | ;;* Programming mode */ | |
382 | ;;*---------------------------------------------------------------------*/ | |
9b047c69 SM |
383 | (defvar flyspell-prog-text-faces |
384 | '(font-lock-string-face font-lock-comment-face font-lock-doc-face) | |
385 | "Faces corresponding to text in programming-mode buffers.") | |
386 | ||
3215afc4 | 387 | (defun flyspell-generic-progmode-verify () |
5c9c3eba | 388 | "Used for `flyspell-generic-check-word-predicate' in programming modes." |
84992dff AM |
389 | ;; (point) is next char after the word. Must check one char before. |
390 | (let ((f (get-text-property (- (point) 1) 'face))) | |
9b047c69 | 391 | (memq f flyspell-prog-text-faces))) |
3215afc4 GM |
392 | |
393 | ;;;###autoload | |
394 | (defun flyspell-prog-mode () | |
395 | "Turn on `flyspell-mode' for comments and strings." | |
396 | (interactive) | |
5c9c3eba RW |
397 | (setq flyspell-generic-check-word-predicate |
398 | 'flyspell-generic-progmode-verify) | |
a8c453e6 RS |
399 | (flyspell-mode 1) |
400 | (run-hooks 'flyspell-prog-mode-hook)) | |
3215afc4 | 401 | |
042c6fb7 SM |
402 | ;;*---------------------------------------------------------------------*/ |
403 | ;;* Overlay compatibility */ | |
404 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
405 | (autoload 'make-overlay "overlay" "Overlay compatibility kit." t) |
406 | (autoload 'overlayp "overlay" "Overlay compatibility kit." t) | |
407 | (autoload 'overlays-in "overlay" "Overlay compatibility kit." t) | |
408 | (autoload 'delete-overlay "overlay" "Overlay compatibility kit." t) | |
409 | (autoload 'overlays-at "overlay" "Overlay compatibility kit." t) | |
410 | (autoload 'overlay-put "overlay" "Overlay compatibility kit." t) | |
411 | (autoload 'overlay-get "overlay" "Overlay compatibility kit." t) | |
412 | (autoload 'previous-overlay-change "overlay" "Overlay compatibility kit." t) | |
60371a2e | 413 | |
042c6fb7 SM |
414 | ;;*---------------------------------------------------------------------*/ |
415 | ;;* The minor mode declaration. */ | |
416 | ;;*---------------------------------------------------------------------*/ | |
2be80b63 DL |
417 | (defvar flyspell-mouse-map |
418 | (let ((map (make-sparse-keymap))) | |
770f7c63 CY |
419 | (if (featurep 'xemacs) |
420 | (define-key map [button2] #'flyspell-correct-word) | |
421 | (define-key map [down-mouse-2] #'flyspell-correct-word) | |
422 | (define-key map [mouse-2] 'undefined)) | |
d4e92f5f RS |
423 | map) |
424 | "Keymap for Flyspell to put on erroneous words.") | |
60371a2e | 425 | |
7ad04640 SM |
426 | (defvar flyspell-mode-map |
427 | (let ((map (make-sparse-keymap))) | |
7ad04640 SM |
428 | (if flyspell-use-meta-tab |
429 | (define-key map "\M-\t" 'flyspell-auto-correct-word)) | |
d4e92f5f RS |
430 | (define-key map flyspell-auto-correct-binding 'flyspell-auto-correct-previous-word) |
431 | (define-key map [(control ?\,)] 'flyspell-goto-next-error) | |
432 | (define-key map [(control ?\.)] 'flyspell-auto-correct-word) | |
89be8f4e | 433 | (define-key map [?\C-c ?$] 'flyspell-correct-word-before-point) |
d4e92f5f RS |
434 | map) |
435 | "Minor mode keymap for Flyspell mode--for the whole buffer.") | |
3215afc4 GM |
436 | |
437 | ;; dash character machinery | |
438 | (defvar flyspell-consider-dash-as-word-delimiter-flag nil | |
fb7ada5f | 439 | "Non-nil means that the `-' char is considered as a word delimiter.") |
3215afc4 GM |
440 | (make-variable-buffer-local 'flyspell-consider-dash-as-word-delimiter-flag) |
441 | (defvar flyspell-dash-dictionary nil) | |
442 | (make-variable-buffer-local 'flyspell-dash-dictionary) | |
443 | (defvar flyspell-dash-local-dictionary nil) | |
444 | (make-variable-buffer-local 'flyspell-dash-local-dictionary) | |
445 | ||
042c6fb7 SM |
446 | ;;*---------------------------------------------------------------------*/ |
447 | ;;* Highlighting */ | |
448 | ;;*---------------------------------------------------------------------*/ | |
c43aed5a | 449 | (defface flyspell-incorrect |
7ad04640 SM |
450 | '((((class color)) (:foreground "OrangeRed" :bold t :underline t)) |
451 | (t (:bold t))) | |
bb578a1d | 452 | "Face used for marking a misspelled word in Flyspell." |
0a67052f | 453 | :group 'flyspell) |
c4f6e489 | 454 | (define-obsolete-face-alias 'flyspell-incorrect-face 'flyspell-incorrect "22.1") |
0a67052f | 455 | |
c43aed5a | 456 | (defface flyspell-duplicate |
7ad04640 SM |
457 | '((((class color)) (:foreground "Gold3" :bold t :underline t)) |
458 | (t (:bold t))) | |
bb578a1d | 459 | "Face used for marking a misspelled word that appears twice in the buffer. |
2ed1f669 | 460 | See also `flyspell-duplicate-distance'." |
0a67052f | 461 | :group 'flyspell) |
c4f6e489 | 462 | (define-obsolete-face-alias 'flyspell-duplicate-face 'flyspell-duplicate "22.1") |
0a67052f | 463 | |
60371a2e RS |
464 | (defvar flyspell-overlay nil) |
465 | ||
042c6fb7 SM |
466 | ;;*---------------------------------------------------------------------*/ |
467 | ;;* flyspell-mode ... */ | |
468 | ;;*---------------------------------------------------------------------*/ | |
3adbe224 | 469 | ;;;###autoload(defvar flyspell-mode nil "Non-nil if Flyspell mode is enabled.") |
60371a2e | 470 | ;;;###autoload |
b978659c | 471 | (define-minor-mode flyspell-mode |
ac6c8639 CY |
472 | "Toggle on-the-fly spell checking (Flyspell mode). |
473 | With a prefix argument ARG, enable Flyspell mode if ARG is | |
474 | positive, and disable it otherwise. If called from Lisp, enable | |
475 | the mode if ARG is omitted or nil. | |
476 | ||
477 | Flyspell mode is a buffer-local minor mode. When enabled, it | |
478 | spawns a single Ispell process and checks each word. The default | |
479 | flyspell behavior is to highlight incorrect words. | |
2fced6f9 | 480 | |
60371a2e RS |
481 | Bindings: |
482 | \\[ispell-word]: correct words (using Ispell). | |
483 | \\[flyspell-auto-correct-word]: automatically correct word. | |
a8c453e6 RS |
484 | \\[flyspell-auto-correct-previous-word]: automatically correct the last misspelled word. |
485 | \\[flyspell-correct-word] (or down-mouse-2): popup correct words. | |
60371a2e RS |
486 | |
487 | Hooks: | |
b224db38 | 488 | This runs `flyspell-mode-hook' after flyspell mode is entered or exit. |
60371a2e RS |
489 | |
490 | Remark: | |
491 | `flyspell-mode' uses `ispell-mode'. Thus all Ispell options are | |
274483cc | 492 | valid. For instance, a different dictionary can be used by |
60371a2e RS |
493 | invoking `ispell-change-dictionary'. |
494 | ||
495 | Consider using the `ispell-parser' to check your text. For instance | |
496 | consider adding: | |
0a67052f | 497 | \(add-hook 'tex-mode-hook (function (lambda () (setq ispell-parser 'tex)))) |
60371a2e RS |
498 | in your .emacs file. |
499 | ||
0dd10e62 RS |
500 | \\[flyspell-region] checks all words inside a region. |
501 | \\[flyspell-buffer] checks the whole buffer." | |
b978659c LK |
502 | :lighter flyspell-mode-line-string |
503 | :keymap flyspell-mode-map | |
504 | :group 'flyspell | |
505 | (if flyspell-mode | |
7dfc15df | 506 | (condition-case err |
478adae2 | 507 | (flyspell-mode-on) |
7dfc15df | 508 | (error (message "Error enabling Flyspell mode:\n%s" (cdr err)) |
478adae2 | 509 | (flyspell-mode -1))) |
b978659c | 510 | (flyspell-mode-off))) |
3215afc4 | 511 | |
2809db33 RS |
512 | ;;;###autoload |
513 | (defun turn-on-flyspell () | |
514 | "Unconditionally turn on Flyspell mode." | |
515 | (flyspell-mode 1)) | |
516 | ||
517 | ;;;###autoload | |
518 | (defun turn-off-flyspell () | |
519 | "Unconditionally turn off Flyspell mode." | |
520 | (flyspell-mode -1)) | |
521 | ||
522 | (custom-add-option 'text-mode-hook 'turn-on-flyspell) | |
523 | ||
042c6fb7 SM |
524 | ;;*---------------------------------------------------------------------*/ |
525 | ;;* flyspell-buffers ... */ | |
526 | ;;* ------------------------------------------------------------- */ | |
527 | ;;* For remembering buffers running flyspell */ | |
528 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 | 529 | (defvar flyspell-buffers nil) |
2fced6f9 | 530 | |
042c6fb7 SM |
531 | ;;*---------------------------------------------------------------------*/ |
532 | ;;* flyspell-minibuffer-p ... */ | |
533 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
534 | (defun flyspell-minibuffer-p (buffer) |
535 | "Is BUFFER a minibuffer?" | |
536 | (let ((ws (get-buffer-window-list buffer t))) | |
537 | (and (consp ws) (window-minibuffer-p (car ws))))) | |
538 | ||
042c6fb7 SM |
539 | ;;*---------------------------------------------------------------------*/ |
540 | ;;* flyspell-accept-buffer-local-defs ... */ | |
541 | ;;*---------------------------------------------------------------------*/ | |
6a99c272 SM |
542 | (defvar flyspell-last-buffer nil |
543 | "The buffer in which the last flyspell operation took place.") | |
544 | ||
bef9f82c | 545 | (defun flyspell-accept-buffer-local-defs (&optional force) |
6a99c272 SM |
546 | ;; When flyspell-word is used inside a loop (e.g. when processing |
547 | ;; flyspell-changes), the calls to `ispell-accept-buffer-local-defs' end | |
548 | ;; up dwarfing everything else, so only do it when the buffer has changed. | |
bef9f82c | 549 | (when (or force (not (eq flyspell-last-buffer (current-buffer)))) |
6a99c272 SM |
550 | (setq flyspell-last-buffer (current-buffer)) |
551 | ;; Strange problem: If buffer in current window has font-lock turned on, | |
552 | ;; but SET-BUFFER was called to point to an invisible buffer, this ispell | |
553 | ;; call will reset the buffer to the buffer in the current window. | |
554 | ;; However, it only happens at startup (fix by Albert L. Ting). | |
555 | (save-current-buffer | |
556 | (ispell-accept-buffer-local-defs)) | |
557 | (unless (and (eq flyspell-dash-dictionary ispell-dictionary) | |
558 | (eq flyspell-dash-local-dictionary ispell-local-dictionary)) | |
a8c453e6 | 559 | ;; The dictionary has changed |
6a99c272 SM |
560 | (setq flyspell-dash-dictionary ispell-dictionary) |
561 | (setq flyspell-dash-local-dictionary ispell-local-dictionary) | |
562 | (setq flyspell-consider-dash-as-word-delimiter-flag | |
563 | (member (or ispell-local-dictionary ispell-dictionary) | |
564 | flyspell-dictionaries-that-consider-dash-as-word-delimiter))))) | |
3215afc4 | 565 | |
c1ac9f5e EZ |
566 | (defun flyspell-hack-local-variables-hook () |
567 | ;; When local variables are loaded, see if the dictionary context | |
568 | ;; has changed. | |
569 | (flyspell-accept-buffer-local-defs 'force)) | |
570 | ||
ef0f5f7e SM |
571 | (defun flyspell-kill-ispell-hook () |
572 | (setq flyspell-last-buffer nil) | |
573 | (dolist (buf (buffer-list)) | |
949855fe SM |
574 | (with-current-buffer buf |
575 | (kill-local-variable 'flyspell-word-cache-word)))) | |
ef0f5f7e | 576 | |
8e2e2956 SM |
577 | ;; Make sure we flush our caches when needed. Do it here rather than in |
578 | ;; flyspell-mode-on, since flyspell-region may be used without ever turning | |
579 | ;; on flyspell-mode. | |
580 | (add-hook 'ispell-kill-ispell-hook 'flyspell-kill-ispell-hook) | |
581 | ||
042c6fb7 SM |
582 | ;;*---------------------------------------------------------------------*/ |
583 | ;;* flyspell-mode-on ... */ | |
584 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 585 | (defun flyspell-mode-on () |
59e7a637 | 586 | "Turn Flyspell mode on. Do not use this; use `flyspell-mode' instead." |
caea54f8 | 587 | (ispell-set-spellchecker-params) ; Initialize variables and dicts alists |
c43aed5a | 588 | (setq ispell-highlight-face 'flyspell-incorrect) |
3215afc4 | 589 | ;; local dictionaries setup |
8ceb0c6e RS |
590 | (or ispell-local-dictionary ispell-dictionary |
591 | (if flyspell-default-dictionary | |
592 | (ispell-change-dictionary flyspell-default-dictionary))) | |
3215afc4 GM |
593 | ;; we have to force ispell to accept the local definition or |
594 | ;; otherwise it could be too late, the local dictionary may | |
595 | ;; be forgotten! | |
bef9f82c SM |
596 | ;; Pass the `force' argument for the case where flyspell was active already |
597 | ;; but the buffer's local-defs have been edited. | |
598 | (flyspell-accept-buffer-local-defs 'force) | |
58c68129 | 599 | ;; we put the `flyspell-delayed' property on some commands |
60371a2e | 600 | (flyspell-delay-commands) |
58c68129 | 601 | ;; we put the `flyspell-deplacement' property on some commands |
3215afc4 | 602 | (flyspell-deplacement-commands) |
60371a2e | 603 | ;; we bound flyspell action to post-command hook |
2ed1f669 | 604 | (add-hook 'post-command-hook (function flyspell-post-command-hook) t t) |
60371a2e | 605 | ;; we bound flyspell action to pre-command hook |
2ed1f669 | 606 | (add-hook 'pre-command-hook (function flyspell-pre-command-hook) t t) |
3215afc4 | 607 | ;; we bound flyspell action to after-change hook |
6a99c272 | 608 | (add-hook 'after-change-functions 'flyspell-after-change-function nil t) |
c1ac9f5e EZ |
609 | ;; we bound flyspell action to hack-local-variables-hook |
610 | (add-hook 'hack-local-variables-hook | |
611 | (function flyspell-hack-local-variables-hook) t t) | |
5c9c3eba | 612 | ;; set flyspell-generic-check-word-predicate based on the major mode |
0a67052f RS |
613 | (let ((mode-predicate (get major-mode 'flyspell-mode-predicate))) |
614 | (if mode-predicate | |
5c9c3eba | 615 | (setq flyspell-generic-check-word-predicate mode-predicate))) |
60371a2e | 616 | ;; the welcome message |
73194d67 PJ |
617 | (if (and flyspell-issue-message-flag |
618 | flyspell-issue-welcome-flag | |
32226619 | 619 | (called-interactively-p 'interactive)) |
65a5c06a RS |
620 | (let ((binding (where-is-internal 'flyspell-auto-correct-word |
621 | nil 'non-ascii))) | |
5673af85 | 622 | (message "%s" |
65a5c06a | 623 | (if binding |
3215afc4 | 624 | (format "Welcome to flyspell. Use %s or Mouse-2 to correct words." |
65a5c06a | 625 | (key-description binding)) |
b224db38 | 626 | "Welcome to flyspell. Use Mouse-2 to correct words."))))) |
60371a2e | 627 | |
042c6fb7 SM |
628 | ;;*---------------------------------------------------------------------*/ |
629 | ;;* flyspell-delay-commands ... */ | |
630 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 631 | (defun flyspell-delay-commands () |
59e7a637 | 632 | "Install the standard set of Flyspell delayed commands." |
6c3bce72 | 633 | (mapc 'flyspell-delay-command flyspell-default-delayed-commands) |
60371a2e RS |
634 | (mapcar 'flyspell-delay-command flyspell-delayed-commands)) |
635 | ||
042c6fb7 SM |
636 | ;;*---------------------------------------------------------------------*/ |
637 | ;;* flyspell-delay-command ... */ | |
638 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 639 | (defun flyspell-delay-command (command) |
59e7a637 | 640 | "Set COMMAND to be delayed, for Flyspell. |
60371a2e | 641 | When flyspell `post-command-hook' is invoked because a delayed command |
25f2ad05 | 642 | as been used the current word is not immediately checked. |
0a67052f RS |
643 | It will be checked only after `flyspell-delay' seconds." |
644 | (interactive "SDelay Flyspell after Command: ") | |
60371a2e RS |
645 | (put command 'flyspell-delayed t)) |
646 | ||
042c6fb7 SM |
647 | ;;*---------------------------------------------------------------------*/ |
648 | ;;* flyspell-deplacement-commands ... */ | |
649 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
650 | (defun flyspell-deplacement-commands () |
651 | "Install the standard set of Flyspell deplacement commands." | |
6c3bce72 | 652 | (mapc 'flyspell-deplacement-command flyspell-default-deplacement-commands) |
3215afc4 | 653 | (mapcar 'flyspell-deplacement-command flyspell-deplacement-commands)) |
60371a2e | 654 | |
042c6fb7 SM |
655 | ;;*---------------------------------------------------------------------*/ |
656 | ;;* flyspell-deplacement-command ... */ | |
657 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
658 | (defun flyspell-deplacement-command (command) |
659 | "Set COMMAND that implement cursor movements, for Flyspell. | |
660 | When flyspell `post-command-hook' is invoked because of a deplacement command | |
661 | as been used the current word is checked only if the previous command was | |
662 | not the very same deplacement command." | |
663 | (interactive "SDeplacement Flyspell after Command: ") | |
664 | (put command 'flyspell-deplacement t)) | |
60371a2e | 665 | |
042c6fb7 SM |
666 | ;;*---------------------------------------------------------------------*/ |
667 | ;;* flyspell-word-cache ... */ | |
668 | ;;*---------------------------------------------------------------------*/ | |
60371a2e RS |
669 | (defvar flyspell-word-cache-start nil) |
670 | (defvar flyspell-word-cache-end nil) | |
671 | (defvar flyspell-word-cache-word nil) | |
a8c453e6 | 672 | (defvar flyspell-word-cache-result '_) |
60371a2e RS |
673 | (make-variable-buffer-local 'flyspell-word-cache-start) |
674 | (make-variable-buffer-local 'flyspell-word-cache-end) | |
675 | (make-variable-buffer-local 'flyspell-word-cache-word) | |
a8c453e6 | 676 | (make-variable-buffer-local 'flyspell-word-cache-result) |
60371a2e | 677 | |
042c6fb7 SM |
678 | ;;*---------------------------------------------------------------------*/ |
679 | ;;* The flyspell pre-hook, store the current position. In the */ | |
680 | ;;* post command hook, we will check, if the word at this position */ | |
681 | ;;* has to be spell checked. */ | |
682 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
683 | (defvar flyspell-pre-buffer nil) |
684 | (defvar flyspell-pre-point nil) | |
685 | (defvar flyspell-pre-column nil) | |
686 | (defvar flyspell-pre-pre-buffer nil) | |
687 | (defvar flyspell-pre-pre-point nil) | |
ca0ebecc | 688 | (make-variable-buffer-local 'flyspell-pre-point) |
3215afc4 | 689 | |
042c6fb7 SM |
690 | ;;*---------------------------------------------------------------------*/ |
691 | ;;* flyspell-previous-command ... */ | |
692 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
693 | (defvar flyspell-previous-command nil |
694 | "The last interactive command checked by Flyspell.") | |
60371a2e | 695 | |
042c6fb7 SM |
696 | ;;*---------------------------------------------------------------------*/ |
697 | ;;* flyspell-pre-command-hook ... */ | |
698 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 699 | (defun flyspell-pre-command-hook () |
0a67052f | 700 | "Save the current buffer and point for Flyspell's post-command hook." |
60371a2e RS |
701 | (interactive) |
702 | (setq flyspell-pre-buffer (current-buffer)) | |
3215afc4 GM |
703 | (setq flyspell-pre-point (point)) |
704 | (setq flyspell-pre-column (current-column))) | |
60371a2e | 705 | |
042c6fb7 SM |
706 | ;;*---------------------------------------------------------------------*/ |
707 | ;;* flyspell-mode-off ... */ | |
708 | ;;*---------------------------------------------------------------------*/ | |
59e7a637 | 709 | ;;;###autoload |
60371a2e | 710 | (defun flyspell-mode-off () |
59e7a637 | 711 | "Turn Flyspell mode off." |
60371a2e | 712 | ;; we remove the hooks |
2ed1f669 RS |
713 | (remove-hook 'post-command-hook (function flyspell-post-command-hook) t) |
714 | (remove-hook 'pre-command-hook (function flyspell-pre-command-hook) t) | |
6a99c272 | 715 | (remove-hook 'after-change-functions 'flyspell-after-change-function t) |
c1ac9f5e EZ |
716 | (remove-hook 'hack-local-variables-hook |
717 | (function flyspell-hack-local-variables-hook) t) | |
3ed8598c | 718 | ;; we remove all the flyspell highlightings |
60371a2e RS |
719 | (flyspell-delete-all-overlays) |
720 | ;; we have to erase pre cache variables | |
721 | (setq flyspell-pre-buffer nil) | |
722 | (setq flyspell-pre-point nil) | |
723 | ;; we mark the mode as killed | |
724 | (setq flyspell-mode nil)) | |
725 | ||
042c6fb7 SM |
726 | ;;*---------------------------------------------------------------------*/ |
727 | ;;* flyspell-check-pre-word-p ... */ | |
728 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 729 | (defun flyspell-check-pre-word-p () |
a8c453e6 | 730 | "Return non-nil if we should check the word before point. |
0a67052f RS |
731 | More precisely, it applies to the word that was before point |
732 | before the current command." | |
60371a2e RS |
733 | (cond |
734 | ((or (not (numberp flyspell-pre-point)) | |
735 | (not (bufferp flyspell-pre-buffer)) | |
736 | (not (buffer-live-p flyspell-pre-buffer))) | |
737 | nil) | |
3215afc4 GM |
738 | ((and (eq flyspell-pre-pre-point flyspell-pre-point) |
739 | (eq flyspell-pre-pre-buffer flyspell-pre-buffer)) | |
740 | nil) | |
65a5c06a RS |
741 | ((or (and (= flyspell-pre-point (- (point) 1)) |
742 | (eq (char-syntax (char-after flyspell-pre-point)) ?w)) | |
60371a2e RS |
743 | (= flyspell-pre-point (point)) |
744 | (= flyspell-pre-point (+ (point) 1))) | |
745 | nil) | |
3215afc4 | 746 | ((and (symbolp this-command) |
f30fe18b | 747 | (not executing-kbd-macro) |
3215afc4 GM |
748 | (or (get this-command 'flyspell-delayed) |
749 | (and (get this-command 'flyspell-deplacement) | |
750 | (eq flyspell-previous-command this-command))) | |
751 | (or (= (current-column) 0) | |
752 | (= (current-column) flyspell-pre-column) | |
6797fabd CY |
753 | ;; If other post-command-hooks change the buffer, |
754 | ;; flyspell-pre-point can lie past eob (bug#468). | |
755 | (null (char-after flyspell-pre-point)) | |
3215afc4 GM |
756 | (eq (char-syntax (char-after flyspell-pre-point)) ?w))) |
757 | nil) | |
60371a2e RS |
758 | ((not (eq (current-buffer) flyspell-pre-buffer)) |
759 | t) | |
760 | ((not (and (numberp flyspell-word-cache-start) | |
761 | (numberp flyspell-word-cache-end))) | |
762 | t) | |
763 | (t | |
764 | (or (< flyspell-pre-point flyspell-word-cache-start) | |
765 | (> flyspell-pre-point flyspell-word-cache-end))))) | |
3215afc4 | 766 | |
042c6fb7 SM |
767 | ;;*---------------------------------------------------------------------*/ |
768 | ;;* The flyspell after-change-hook, store the change position. In */ | |
769 | ;;* the post command hook, we will check, if the word at this */ | |
770 | ;;* position has to be spell checked. */ | |
771 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 | 772 | (defvar flyspell-changes nil) |
6a99c272 | 773 | (make-variable-buffer-local 'flyspell-changes) |
3215afc4 | 774 | |
042c6fb7 SM |
775 | ;;*---------------------------------------------------------------------*/ |
776 | ;;* flyspell-after-change-function ... */ | |
777 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
778 | (defun flyspell-after-change-function (start stop len) |
779 | "Save the current buffer and point for Flyspell's post-command hook." | |
6a99c272 | 780 | (push (cons start stop) flyspell-changes)) |
3215afc4 | 781 | |
042c6fb7 SM |
782 | ;;*---------------------------------------------------------------------*/ |
783 | ;;* flyspell-check-changed-word-p ... */ | |
784 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
785 | (defun flyspell-check-changed-word-p (start stop) |
786 | "Return t when the changed word has to be checked. | |
787 | The answer depends of several criteria. | |
788 | Mostly we check word delimiters." | |
789 | (cond | |
a8c453e6 | 790 | ((and (memq (char-after start) '(?\n ? )) (> stop start)) |
3215afc4 GM |
791 | t) |
792 | ((not (numberp flyspell-pre-point)) | |
793 | t) | |
794 | ((and (>= flyspell-pre-point start) (<= flyspell-pre-point stop)) | |
795 | nil) | |
796 | ((let ((pos (point))) | |
797 | (or (>= pos start) (<= pos stop) (= pos (1+ stop)))) | |
798 | nil) | |
799 | (t | |
800 | t))) | |
801 | ||
042c6fb7 SM |
802 | ;;*---------------------------------------------------------------------*/ |
803 | ;;* flyspell-check-word-p ... */ | |
804 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
805 | (defun flyspell-check-word-p () |
806 | "Return t when the word at `point' has to be checked. | |
807 | The answer depends of several criteria. | |
808 | Mostly we check word delimiters." | |
809 | (cond | |
810 | ((<= (- (point-max) 1) (point-min)) | |
811 | ;; the buffer is not filled enough | |
812 | nil) | |
813 | ((and (and (> (current-column) 0) | |
814 | (not (eq (current-column) flyspell-pre-column))) | |
815 | (save-excursion | |
816 | (backward-char 1) | |
817 | (and (looking-at (flyspell-get-not-casechars)) | |
818 | (or flyspell-consider-dash-as-word-delimiter-flag | |
9c841316 | 819 | (not (looking-at "-")))))) |
3215afc4 GM |
820 | ;; yes because we have reached or typed a word delimiter. |
821 | t) | |
822 | ((symbolp this-command) | |
823 | (cond | |
824 | ((get this-command 'flyspell-deplacement) | |
825 | (not (eq flyspell-previous-command this-command))) | |
826 | ((get this-command 'flyspell-delayed) | |
827 | ;; the current command is not delayed, that | |
828 | ;; is that we must check the word now | |
1fb7ce77 RS |
829 | (and (not unread-command-events) |
830 | (sit-for flyspell-delay))) | |
3215afc4 GM |
831 | (t t))) |
832 | (t t))) | |
833 | ||
042c6fb7 SM |
834 | ;;*---------------------------------------------------------------------*/ |
835 | ;;* flyspell-debug-signal-no-check ... */ | |
836 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
837 | (defun flyspell-debug-signal-no-check (msg obj) |
838 | (setq debug-on-error t) | |
042c6fb7 SM |
839 | (with-current-buffer (get-buffer-create "*flyspell-debug*") |
840 | (erase-buffer) | |
841 | (insert "NO-CHECK:\n") | |
842 | (insert (format " %S : %S\n" msg obj)))) | |
843 | ||
844 | ;;*---------------------------------------------------------------------*/ | |
845 | ;;* flyspell-debug-signal-pre-word-checked ... */ | |
846 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
847 | (defun flyspell-debug-signal-pre-word-checked () |
848 | (setq debug-on-error t) | |
042c6fb7 SM |
849 | (with-current-buffer (get-buffer-create "*flyspell-debug*") |
850 | (insert "PRE-WORD:\n") | |
851 | (insert (format " pre-point : %S\n" flyspell-pre-point)) | |
852 | (insert (format " pre-buffer : %S\n" flyspell-pre-buffer)) | |
853 | (insert (format " cache-start: %S\n" flyspell-word-cache-start)) | |
854 | (insert (format " cache-end : %S\n" flyspell-word-cache-end)) | |
855 | (goto-char (point-max)))) | |
856 | ||
857 | ;;*---------------------------------------------------------------------*/ | |
858 | ;;* flyspell-debug-signal-word-checked ... */ | |
859 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
860 | (defun flyspell-debug-signal-word-checked () |
861 | (setq debug-on-error t) | |
042c6fb7 SM |
862 | (let ((oldbuf (current-buffer)) |
863 | (point (point))) | |
864 | (with-current-buffer (get-buffer-create "*flyspell-debug*") | |
3215afc4 GM |
865 | (insert "WORD:\n") |
866 | (insert (format " this-cmd : %S\n" this-command)) | |
867 | (insert (format " delayed : %S\n" (and (symbolp this-command) | |
868 | (get this-command 'flyspell-delayed)))) | |
869 | (insert (format " point : %S\n" point)) | |
870 | (insert (format " prev-char : [%c] %S\n" | |
042c6fb7 | 871 | (with-current-buffer oldbuf |
3215afc4 GM |
872 | (let ((c (if (> (point) (point-min)) |
873 | (save-excursion | |
874 | (backward-char 1) | |
875 | (char-after (point))) | |
876 | ? ))) | |
3215afc4 | 877 | c)) |
042c6fb7 | 878 | (with-current-buffer oldbuf |
3215afc4 GM |
879 | (let ((c (if (> (point) (point-min)) |
880 | (save-excursion | |
881 | (backward-char 1) | |
882 | (and (and (looking-at (flyspell-get-not-casechars)) 1) | |
883 | (and (or flyspell-consider-dash-as-word-delimiter-flag | |
884 | (not (looking-at "\\-"))) 2)))))) | |
3215afc4 GM |
885 | c)))) |
886 | (insert (format " because : %S\n" | |
887 | (cond | |
888 | ((not (and (symbolp this-command) | |
889 | (get this-command 'flyspell-delayed))) | |
890 | ;; the current command is not delayed, that | |
891 | ;; is that we must check the word now | |
892 | 'not-delayed) | |
042c6fb7 | 893 | ((with-current-buffer oldbuf |
3215afc4 GM |
894 | (let ((c (if (> (point) (point-min)) |
895 | (save-excursion | |
896 | (backward-char 1) | |
897 | (and (looking-at (flyspell-get-not-casechars)) | |
898 | (or flyspell-consider-dash-as-word-delimiter-flag | |
899 | (not (looking-at "\\-")))))))) | |
3215afc4 GM |
900 | c)) |
901 | ;; yes because we have reached or typed a word delimiter. | |
902 | 'separator) | |
903 | ((not (integerp flyspell-delay)) | |
904 | ;; yes because the user had set up a no-delay configuration. | |
905 | 'no-delay) | |
906 | (t | |
907 | 'sit-for)))) | |
908 | (goto-char (point-max))))) | |
909 | ||
042c6fb7 SM |
910 | ;;*---------------------------------------------------------------------*/ |
911 | ;;* flyspell-debug-signal-changed-checked ... */ | |
912 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
913 | (defun flyspell-debug-signal-changed-checked () |
914 | (setq debug-on-error t) | |
042c6fb7 SM |
915 | (let ((point (point))) |
916 | (with-current-buffer (get-buffer-create "*flyspell-debug*") | |
3215afc4 GM |
917 | (insert "CHANGED WORD:\n") |
918 | (insert (format " point : %S\n" point)) | |
919 | (goto-char (point-max))))) | |
920 | ||
042c6fb7 SM |
921 | ;;*---------------------------------------------------------------------*/ |
922 | ;;* flyspell-post-command-hook ... */ | |
923 | ;;* ------------------------------------------------------------- */ | |
924 | ;;* It is possible that we check several words: */ | |
925 | ;;* 1- the current word is checked if the predicate */ | |
926 | ;;* FLYSPELL-CHECK-WORD-P is true */ | |
927 | ;;* 2- the word that used to be the current word before the */ | |
928 | ;;* THIS-COMMAND is checked if: */ | |
929 | ;;* a- the previous word is different from the current word */ | |
930 | ;;* b- the previous word as not just been checked by the */ | |
931 | ;;* previous FLYSPELL-POST-COMMAND-HOOK */ | |
932 | ;;* 3- the words changed by the THIS-COMMAND that are neither the */ | |
933 | ;;* previous word nor the current word */ | |
934 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 935 | (defun flyspell-post-command-hook () |
e1b0b23a | 936 | "The `post-command-hook' used by flyspell to check a word on-the-fly." |
60371a2e | 937 | (interactive) |
ca0ebecc | 938 | (when flyspell-mode |
e1b0b23a SM |
939 | (with-local-quit |
940 | (let ((command this-command) | |
941 | ;; Prevent anything we do from affecting the mark. | |
942 | deactivate-mark) | |
943 | (if (flyspell-check-pre-word-p) | |
944 | (with-current-buffer flyspell-pre-buffer | |
945 | '(flyspell-debug-signal-pre-word-checked) | |
946 | (save-excursion | |
947 | (goto-char flyspell-pre-point) | |
948 | (flyspell-word)))) | |
949 | (if (flyspell-check-word-p) | |
950 | (progn | |
951 | '(flyspell-debug-signal-word-checked) | |
952 | ;; FIXME: This should be asynchronous! | |
953 | (flyspell-word) | |
954 | ;; we remember which word we have just checked. | |
955 | ;; this will be used next time we will check a word | |
956 | ;; to compare the next current word with the word | |
957 | ;; that as been registered in the pre-command-hook | |
958 | ;; that is these variables are used within the predicate | |
959 | ;; FLYSPELL-CHECK-PRE-WORD-P | |
960 | (setq flyspell-pre-pre-buffer (current-buffer)) | |
961 | (setq flyspell-pre-pre-point (point))) | |
962 | (progn | |
963 | (setq flyspell-pre-pre-buffer nil) | |
964 | (setq flyspell-pre-pre-point nil) | |
965 | ;; when a word is not checked because of a delayed command | |
966 | ;; we do not disable the ispell cache. | |
967 | (if (and (symbolp this-command) | |
968 | (get this-command 'flyspell-delayed)) | |
969 | (progn | |
970 | (setq flyspell-word-cache-end -1) | |
971 | (setq flyspell-word-cache-result '_))))) | |
972 | (while (and (not (input-pending-p)) (consp flyspell-changes)) | |
973 | (let ((start (car (car flyspell-changes))) | |
974 | (stop (cdr (car flyspell-changes)))) | |
975 | (if (flyspell-check-changed-word-p start stop) | |
976 | (save-excursion | |
977 | '(flyspell-debug-signal-changed-checked) | |
978 | (goto-char start) | |
979 | (flyspell-word))) | |
980 | (setq flyspell-changes (cdr flyspell-changes)))) | |
981 | (setq flyspell-previous-command command))))) | |
3215afc4 | 982 | |
042c6fb7 SM |
983 | ;;*---------------------------------------------------------------------*/ |
984 | ;;* flyspell-notify-misspell ... */ | |
985 | ;;*---------------------------------------------------------------------*/ | |
986 | (defun flyspell-notify-misspell (word poss) | |
3215afc4 GM |
987 | (let ((replacements (if (stringp poss) |
988 | poss | |
989 | (if flyspell-sort-corrections | |
990 | (sort (car (cdr (cdr poss))) 'string<) | |
991 | (car (cdr (cdr poss))))))) | |
73194d67 | 992 | (if flyspell-issue-message-flag |
b8b7c66e | 993 | (message "misspelling `%s' %S" word replacements)))) |
60371a2e | 994 | |
042c6fb7 SM |
995 | ;;*---------------------------------------------------------------------*/ |
996 | ;;* flyspell-word-search-backward ... */ | |
997 | ;;*---------------------------------------------------------------------*/ | |
887d14ad | 998 | (defun flyspell-word-search-backward (word bound &optional ignore-case) |
a8c453e6 RS |
999 | (save-excursion |
1000 | (let ((r '()) | |
15d8dc8b | 1001 | (inhibit-point-motion-hooks t) |
a8c453e6 RS |
1002 | p) |
1003 | (while (and (not r) (setq p (search-backward word bound t))) | |
f3e3a990 | 1004 | (let ((lw (flyspell-get-word))) |
887d14ad LMI |
1005 | (if (and (consp lw) |
1006 | (if ignore-case | |
d224ac83 | 1007 | (string-equal (downcase (car lw)) (downcase word)) |
887d14ad | 1008 | (string-equal (car lw) word))) |
a8c453e6 RS |
1009 | (setq r p) |
1010 | (goto-char p)))) | |
1011 | r))) | |
ebb7de84 | 1012 | |
042c6fb7 SM |
1013 | ;;*---------------------------------------------------------------------*/ |
1014 | ;;* flyspell-word-search-forward ... */ | |
1015 | ;;*---------------------------------------------------------------------*/ | |
a8c453e6 RS |
1016 | (defun flyspell-word-search-forward (word bound) |
1017 | (save-excursion | |
1018 | (let ((r '()) | |
15d8dc8b | 1019 | (inhibit-point-motion-hooks t) |
a8c453e6 RS |
1020 | p) |
1021 | (while (and (not r) (setq p (search-forward word bound t))) | |
f3e3a990 | 1022 | (let ((lw (flyspell-get-word))) |
a8c453e6 RS |
1023 | (if (and (consp lw) (string-equal (car lw) word)) |
1024 | (setq r p) | |
1025 | (goto-char (1+ p))))) | |
1026 | r))) | |
ebb7de84 | 1027 | |
042c6fb7 SM |
1028 | ;;*---------------------------------------------------------------------*/ |
1029 | ;;* flyspell-word ... */ | |
1030 | ;;*---------------------------------------------------------------------*/ | |
bd4532fc | 1031 | (defun flyspell-word (&optional following known-misspelling) |
c2e161b2 GM |
1032 | "Spell check a word. |
1033 | If the optional argument FOLLOWING, or, when called interactively | |
1034 | `ispell-following-word', is non-nil, checks the following (rather | |
bd4532fc AM |
1035 | than preceding) word when the cursor is not over a word. If |
1036 | optional argument KNOWN-MISSPELLING is non nil considers word a | |
1037 | misspelling and skips redundant spell-checking step." | |
5a2045ce | 1038 | (interactive (list ispell-following-word)) |
caea54f8 | 1039 | (ispell-set-spellchecker-params) ; Initialize variables and dicts alists |
60371a2e | 1040 | (save-excursion |
3215afc4 | 1041 | ;; use the correct dictionary |
1f44857f | 1042 | (flyspell-accept-buffer-local-defs) |
3215afc4 | 1043 | (let* ((cursor-location (point)) |
042c6fb7 | 1044 | (flyspell-word (flyspell-get-word following)) |
9c0fad71 | 1045 | start end poss word ispell-filter) |
3215afc4 | 1046 | (if (or (eq flyspell-word nil) |
5c9c3eba RW |
1047 | (and (fboundp flyspell-generic-check-word-predicate) |
1048 | (not (funcall flyspell-generic-check-word-predicate)))) | |
a8c453e6 | 1049 | t |
60371a2e | 1050 | (progn |
3215afc4 GM |
1051 | ;; destructure return flyspell-word info list. |
1052 | (setq start (car (cdr flyspell-word)) | |
1053 | end (car (cdr (cdr flyspell-word))) | |
1054 | word (car flyspell-word)) | |
60371a2e RS |
1055 | ;; before checking in the directory, we check for doublons. |
1056 | (cond | |
3215afc4 | 1057 | ((and (or (not (eq ispell-parser 'tex)) |
a8c453e6 | 1058 | (and (> start (point-min)) |
7ad04640 | 1059 | (not (memq (char-after (1- start)) '(?\} ?\\))))) |
3215afc4 | 1060 | flyspell-mark-duplications-flag |
08fea928 | 1061 | (not (catch 'exception |
c11325f7 CY |
1062 | (let ((dict (or ispell-local-dictionary |
1063 | ispell-dictionary))) | |
1064 | (dolist (except flyspell-mark-duplications-exceptions) | |
1065 | (and (or (null (car except)) | |
1066 | (and (stringp dict) | |
1067 | (string-match (car except) dict))) | |
1068 | (member (downcase word) (cdr except)) | |
1069 | (throw 'exception t)))))) | |
60371a2e | 1070 | (save-excursion |
6b8aed24 CY |
1071 | (goto-char start) |
1072 | (let* ((bound | |
1073 | (- start | |
1074 | (- end start) | |
1075 | (- (skip-chars-backward " \t\n\f")))) | |
1076 | (p (when (>= bound (point-min)) | |
887d14ad | 1077 | (flyspell-word-search-backward word bound t)))) |
6b8aed24 | 1078 | (and p (/= p start))))) |
60371a2e | 1079 | ;; yes, this is a doublon |
a8c453e6 RS |
1080 | (flyspell-highlight-incorrect-region start end 'doublon) |
1081 | nil) | |
60371a2e RS |
1082 | ((and (eq flyspell-word-cache-start start) |
1083 | (eq flyspell-word-cache-end end) | |
1084 | (string-equal flyspell-word-cache-word word)) | |
1085 | ;; this word had been already checked, we skip | |
a8c453e6 | 1086 | flyspell-word-cache-result) |
60371a2e | 1087 | ((and (eq ispell-parser 'tex) |
3215afc4 | 1088 | (flyspell-tex-command-p flyspell-word)) |
60371a2e RS |
1089 | ;; this is a correct word (because a tex command) |
1090 | (flyspell-unhighlight-at start) | |
1091 | (if (> end start) | |
1092 | (flyspell-unhighlight-at (- end 1))) | |
1093 | t) | |
1094 | (t | |
1095 | ;; we setup the cache | |
1096 | (setq flyspell-word-cache-start start) | |
1097 | (setq flyspell-word-cache-end end) | |
1098 | (setq flyspell-word-cache-word word) | |
1099 | ;; now check spelling of word. | |
bd4532fc AM |
1100 | (if (not known-misspelling) |
1101 | (progn | |
1102 | (ispell-send-string "%\n") | |
1103 | ;; put in verbose mode | |
1104 | (ispell-send-string (concat "^" word "\n")) | |
1105 | ;; we mark the ispell process so it can be killed | |
1106 | ;; when emacs is exited without query | |
1107 | (set-process-query-on-exit-flag ispell-process nil) | |
e1b0b23a SM |
1108 | ;; Wait until ispell has processed word. |
1109 | (while (progn | |
1110 | (accept-process-output ispell-process) | |
1111 | (not (string= "" (car ispell-filter))))) | |
bd4532fc AM |
1112 | ;; (ispell-send-string "!\n") |
1113 | ;; back to terse mode. | |
1114 | ;; Remove leading empty element | |
1115 | (setq ispell-filter (cdr ispell-filter)) | |
1116 | ;; ispell process should return something after word is sent. | |
1117 | ;; Tag word as valid (i.e., skip) otherwise | |
1118 | (or ispell-filter | |
1119 | (setq ispell-filter '(*))) | |
1120 | (if (consp ispell-filter) | |
1121 | (setq poss (ispell-parse-output (car ispell-filter))))) | |
1122 | ;; Else, this was a known misspelling to begin with, and | |
1123 | ;; we should forge an ispell return value. | |
5d2ece3c | 1124 | (setq poss (list word 1 nil nil))) |
a8c453e6 RS |
1125 | (let ((res (cond ((eq poss t) |
1126 | ;; correct | |
1127 | (setq flyspell-word-cache-result t) | |
1128 | (flyspell-unhighlight-at start) | |
1129 | (if (> end start) | |
1130 | (flyspell-unhighlight-at (- end 1))) | |
1131 | t) | |
1132 | ((and (stringp poss) flyspell-highlight-flag) | |
1133 | ;; correct | |
1134 | (setq flyspell-word-cache-result t) | |
1135 | (flyspell-unhighlight-at start) | |
1136 | (if (> end start) | |
1137 | (flyspell-unhighlight-at (- end 1))) | |
1138 | t) | |
1139 | ((null poss) | |
1140 | (setq flyspell-word-cache-result t) | |
1141 | (flyspell-unhighlight-at start) | |
1142 | (if (> end start) | |
1143 | (flyspell-unhighlight-at (- end 1))) | |
1144 | t) | |
1145 | ((or (and (< flyspell-duplicate-distance 0) | |
1146 | (or (save-excursion | |
1147 | (goto-char start) | |
1148 | (flyspell-word-search-backward | |
1149 | word | |
1150 | (point-min))) | |
1151 | (save-excursion | |
1152 | (goto-char end) | |
1153 | (flyspell-word-search-forward | |
1154 | word | |
1155 | (point-max))))) | |
1156 | (and (> flyspell-duplicate-distance 0) | |
1157 | (or (save-excursion | |
1158 | (goto-char start) | |
1159 | (flyspell-word-search-backward | |
1160 | word | |
1161 | (- start | |
1162 | flyspell-duplicate-distance))) | |
1163 | (save-excursion | |
1164 | (goto-char end) | |
1165 | (flyspell-word-search-forward | |
1166 | word | |
1167 | (+ end | |
1168 | flyspell-duplicate-distance)))))) | |
91346f54 RS |
1169 | ;; This is a misspelled word which occurs |
1170 | ;; twice within flyspell-duplicate-distance. | |
a8c453e6 RS |
1171 | (setq flyspell-word-cache-result nil) |
1172 | (if flyspell-highlight-flag | |
1173 | (flyspell-highlight-duplicate-region | |
1174 | start end poss) | |
5673af85 | 1175 | (message "duplicate `%s'" word)) |
a8c453e6 RS |
1176 | nil) |
1177 | (t | |
1178 | (setq flyspell-word-cache-result nil) | |
05bbe066 CY |
1179 | ;; Highlight the location as incorrect, |
1180 | ;; including offset specified in POSS. | |
a8c453e6 RS |
1181 | (if flyspell-highlight-flag |
1182 | (flyspell-highlight-incorrect-region | |
05bbe066 CY |
1183 | (if (and (consp poss) |
1184 | (integerp (nth 1 poss))) | |
1185 | (+ start (nth 1 poss) -1) | |
1186 | start) | |
1187 | end poss) | |
042c6fb7 | 1188 | (flyspell-notify-misspell word poss)) |
a8c453e6 RS |
1189 | nil)))) |
1190 | ;; return to original location | |
ebb7de84 | 1191 | (goto-char cursor-location) |
a8c453e6 RS |
1192 | (if ispell-quit (setq ispell-quit nil)) |
1193 | res)))))))) | |
60371a2e | 1194 | |
042c6fb7 SM |
1195 | ;;*---------------------------------------------------------------------*/ |
1196 | ;;* flyspell-math-tex-command-p ... */ | |
1197 | ;;* ------------------------------------------------------------- */ | |
f768e8e8 CY |
1198 | ;;* This function uses the texmathp package to check if point */ |
1199 | ;;* is within a TeX math environment. `texmathp' can yield errors */ | |
1200 | ;;* if the document is currently not valid TeX syntax. */ | |
042c6fb7 | 1201 | ;;*---------------------------------------------------------------------*/ |
3215afc4 | 1202 | (defun flyspell-math-tex-command-p () |
7ad04640 | 1203 | (when (fboundp 'texmathp) |
f768e8e8 CY |
1204 | (if flyspell-check-tex-math-command |
1205 | nil | |
7ad04640 SM |
1206 | (condition-case nil |
1207 | (texmathp) | |
f768e8e8 | 1208 | (error nil))))) |
3215afc4 | 1209 | |
042c6fb7 SM |
1210 | ;;*---------------------------------------------------------------------*/ |
1211 | ;;* flyspell-tex-command-p ... */ | |
1212 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 1213 | (defun flyspell-tex-command-p (word) |
0a67052f | 1214 | "Return t if WORD is a TeX command." |
3215afc4 GM |
1215 | (or (save-excursion |
1216 | (let ((b (car (cdr word)))) | |
1217 | (and (re-search-backward "\\\\" (- (point) 100) t) | |
1218 | (or (= (match-end 0) b) | |
1219 | (and (goto-char (match-end 0)) | |
1220 | (looking-at flyspell-tex-command-regexp) | |
1221 | (>= (match-end 0) b)))))) | |
1222 | (flyspell-math-tex-command-p))) | |
60371a2e | 1223 | |
042c6fb7 SM |
1224 | ;;*---------------------------------------------------------------------*/ |
1225 | ;;* flyspell-casechars-cache ... */ | |
1226 | ;;*---------------------------------------------------------------------*/ | |
60371a2e RS |
1227 | (defvar flyspell-casechars-cache nil) |
1228 | (defvar flyspell-ispell-casechars-cache nil) | |
1229 | (make-variable-buffer-local 'flyspell-casechars-cache) | |
1230 | (make-variable-buffer-local 'flyspell-ispell-casechars-cache) | |
1231 | ||
042c6fb7 SM |
1232 | ;;*---------------------------------------------------------------------*/ |
1233 | ;;* flyspell-get-casechars ... */ | |
1234 | ;;*---------------------------------------------------------------------*/ | |
60371a2e RS |
1235 | (defun flyspell-get-casechars () |
1236 | "This function builds a string that is the regexp of word chars. | |
0a67052f RS |
1237 | In order to avoid one useless string construction, |
1238 | this function changes the last char of the `ispell-casechars' string." | |
60371a2e RS |
1239 | (let ((ispell-casechars (ispell-get-casechars))) |
1240 | (cond | |
3215afc4 | 1241 | ((eq ispell-parser 'tex) |
60371a2e RS |
1242 | (setq flyspell-ispell-casechars-cache ispell-casechars) |
1243 | (setq flyspell-casechars-cache | |
1244 | (concat (substring ispell-casechars | |
1245 | 0 | |
1246 | (- (length ispell-casechars) 1)) | |
3215afc4 | 1247 | "]")) |
60371a2e RS |
1248 | flyspell-casechars-cache) |
1249 | (t | |
1250 | (setq flyspell-ispell-casechars-cache ispell-casechars) | |
1251 | (setq flyspell-casechars-cache ispell-casechars) | |
1252 | flyspell-casechars-cache)))) | |
2fced6f9 | 1253 | |
042c6fb7 SM |
1254 | ;;*---------------------------------------------------------------------*/ |
1255 | ;;* flyspell-get-not-casechars-cache ... */ | |
1256 | ;;*---------------------------------------------------------------------*/ | |
60371a2e RS |
1257 | (defvar flyspell-not-casechars-cache nil) |
1258 | (defvar flyspell-ispell-not-casechars-cache nil) | |
1259 | (make-variable-buffer-local 'flyspell-not-casechars-cache) | |
1260 | (make-variable-buffer-local 'flyspell-ispell-not-casechars-cache) | |
1261 | ||
042c6fb7 SM |
1262 | ;;*---------------------------------------------------------------------*/ |
1263 | ;;* flyspell-get-not-casechars ... */ | |
1264 | ;;*---------------------------------------------------------------------*/ | |
60371a2e RS |
1265 | (defun flyspell-get-not-casechars () |
1266 | "This function builds a string that is the regexp of non-word chars." | |
1267 | (let ((ispell-not-casechars (ispell-get-not-casechars))) | |
1268 | (cond | |
3215afc4 | 1269 | ((eq ispell-parser 'tex) |
60371a2e RS |
1270 | (setq flyspell-ispell-not-casechars-cache ispell-not-casechars) |
1271 | (setq flyspell-not-casechars-cache | |
1272 | (concat (substring ispell-not-casechars | |
1273 | 0 | |
1274 | (- (length ispell-not-casechars) 1)) | |
3215afc4 | 1275 | "]")) |
60371a2e RS |
1276 | flyspell-not-casechars-cache) |
1277 | (t | |
1278 | (setq flyspell-ispell-not-casechars-cache ispell-not-casechars) | |
1279 | (setq flyspell-not-casechars-cache ispell-not-casechars) | |
1280 | flyspell-not-casechars-cache)))) | |
1281 | ||
042c6fb7 SM |
1282 | ;;*---------------------------------------------------------------------*/ |
1283 | ;;* flyspell-get-word ... */ | |
1284 | ;;*---------------------------------------------------------------------*/ | |
f3e3a990 | 1285 | (defun flyspell-get-word (&optional following extra-otherchars) |
60371a2e | 1286 | "Return the word for spell-checking according to Ispell syntax. |
c2e161b2 GM |
1287 | Optional argument FOLLOWING non-nil means to get the following |
1288 | \(rather than preceding) word when the cursor is not over a word. | |
1289 | Optional second argument EXTRA-OTHERCHARS is a regexp of characters | |
1290 | that may be included as part of a word (see `ispell-dictionary-alist')." | |
60371a2e RS |
1291 | (let* ((flyspell-casechars (flyspell-get-casechars)) |
1292 | (flyspell-not-casechars (flyspell-get-not-casechars)) | |
1293 | (ispell-otherchars (ispell-get-otherchars)) | |
1294 | (ispell-many-otherchars-p (ispell-get-many-otherchars-p)) | |
a8c453e6 RS |
1295 | (word-regexp (concat flyspell-casechars |
1296 | "+\\(" | |
1297 | (if (not (string= "" ispell-otherchars)) | |
1298 | (concat ispell-otherchars "?")) | |
1299 | (if extra-otherchars | |
1300 | (concat extra-otherchars "?")) | |
1301 | flyspell-casechars | |
1302 | "+\\)" | |
1303 | (if (or ispell-many-otherchars-p | |
1304 | extra-otherchars) | |
1305 | "*" "?"))) | |
1306 | did-it-once prevpt | |
60371a2e RS |
1307 | start end word) |
1308 | ;; find the word | |
3215afc4 | 1309 | (if (not (looking-at flyspell-casechars)) |
60371a2e | 1310 | (if following |
9c841316 SM |
1311 | (re-search-forward flyspell-casechars nil t) |
1312 | (re-search-backward flyspell-casechars nil t))) | |
60371a2e | 1313 | ;; move to front of word |
9c841316 | 1314 | (re-search-backward flyspell-not-casechars nil 'start) |
a8c453e6 RS |
1315 | (while (and (or (and (not (string= "" ispell-otherchars)) |
1316 | (looking-at ispell-otherchars)) | |
1317 | (and extra-otherchars (looking-at extra-otherchars))) | |
1318 | (not (bobp)) | |
1319 | (or (not did-it-once) | |
1320 | ispell-many-otherchars-p) | |
1321 | (not (eq prevpt (point)))) | |
1322 | (if (and extra-otherchars (looking-at extra-otherchars)) | |
1323 | (progn | |
9658746b RS |
1324 | (backward-char 1) |
1325 | (if (looking-at flyspell-casechars) | |
9c841316 | 1326 | (re-search-backward flyspell-not-casechars nil 'move))) |
a8c453e6 RS |
1327 | (setq did-it-once t |
1328 | prevpt (point)) | |
1329 | (backward-char 1) | |
1330 | (if (looking-at flyspell-casechars) | |
9c841316 | 1331 | (re-search-backward flyspell-not-casechars nil 'move) |
a8c453e6 | 1332 | (backward-char -1)))) |
60371a2e | 1333 | ;; Now mark the word and save to string. |
9c841316 | 1334 | (if (not (re-search-forward word-regexp nil t)) |
60371a2e RS |
1335 | nil |
1336 | (progn | |
1337 | (setq start (match-beginning 0) | |
1338 | end (point) | |
11570a8f | 1339 | word (buffer-substring-no-properties start end)) |
60371a2e RS |
1340 | (list word start end))))) |
1341 | ||
042c6fb7 SM |
1342 | ;;*---------------------------------------------------------------------*/ |
1343 | ;;* flyspell-small-region ... */ | |
1344 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 | 1345 | (defun flyspell-small-region (beg end) |
60371a2e | 1346 | "Flyspell text between BEG and END." |
60371a2e | 1347 | (save-excursion |
3215afc4 GM |
1348 | (if (> beg end) |
1349 | (let ((old beg)) | |
1350 | (setq beg end) | |
1351 | (setq end old))) | |
60371a2e | 1352 | (goto-char beg) |
1d8a80f0 RS |
1353 | (let ((count 0)) |
1354 | (while (< (point) end) | |
73194d67 | 1355 | (if (and flyspell-issue-message-flag (= count 100)) |
1d8a80f0 RS |
1356 | (progn |
1357 | (message "Spell Checking...%d%%" | |
65a5c06a | 1358 | (* 100 (/ (float (- (point) beg)) (- end beg)))) |
1d8a80f0 RS |
1359 | (setq count 0)) |
1360 | (setq count (+ 1 count))) | |
1361 | (flyspell-word) | |
3215afc4 | 1362 | (sit-for 0) |
1d8a80f0 RS |
1363 | (let ((cur (point))) |
1364 | (forward-word 1) | |
1365 | (if (and (< (point) end) (> (point) (+ cur 1))) | |
1366 | (backward-char 1))))) | |
60371a2e | 1367 | (backward-char 1) |
73194d67 | 1368 | (if flyspell-issue-message-flag (message "Spell Checking completed.")) |
60371a2e RS |
1369 | (flyspell-word))) |
1370 | ||
042c6fb7 SM |
1371 | ;;*---------------------------------------------------------------------*/ |
1372 | ;;* flyspell-external-ispell-process ... */ | |
1373 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 | 1374 | (defvar flyspell-external-ispell-process '() |
1f44857f | 1375 | "The external Flyspell Ispell process.") |
3215afc4 | 1376 | |
042c6fb7 SM |
1377 | ;;*---------------------------------------------------------------------*/ |
1378 | ;;* flyspell-external-ispell-buffer ... */ | |
1379 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
1380 | (defvar flyspell-external-ispell-buffer '()) |
1381 | (defvar flyspell-large-region-buffer '()) | |
1382 | (defvar flyspell-large-region-beg (point-min)) | |
1383 | (defvar flyspell-large-region-end (point-max)) | |
1384 | ||
042c6fb7 SM |
1385 | ;;*---------------------------------------------------------------------*/ |
1386 | ;;* flyspell-external-point-words ... */ | |
1387 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 | 1388 | (defun flyspell-external-point-words () |
cc8556d9 RS |
1389 | "Mark words from a buffer listing incorrect words in order of appearance. |
1390 | The list of incorrect words should be in `flyspell-external-ispell-buffer'. | |
1391 | \(We finish by killing that buffer and setting the variable to nil.) | |
1392 | The buffer to mark them in is `flyspell-large-region-buffer'." | |
27e0edcd | 1393 | (let (words-not-found |
51978cac | 1394 | (ispell-otherchars (ispell-get-otherchars)) |
ee65a132 | 1395 | (buffer-scan-pos flyspell-large-region-beg) |
55faab0a | 1396 | case-fold-search) |
27e0edcd EZ |
1397 | (with-current-buffer flyspell-external-ispell-buffer |
1398 | (goto-char (point-min)) | |
51978cac RS |
1399 | ;; Loop over incorrect words, in the order they were reported, |
1400 | ;; which is also the order they appear in the buffer being checked. | |
9c841316 | 1401 | (while (re-search-forward "\\([^\n]+\\)\n" nil t) |
27e0edcd EZ |
1402 | ;; Bind WORD to the next one. |
1403 | (let ((word (match-string 1)) (wordpos (point))) | |
1404 | ;; Here there used to be code to see if WORD is the same | |
1405 | ;; as the previous iteration, and count the number of consecutive | |
1406 | ;; identical words, and the loop below would search for that many. | |
1407 | ;; That code seemed to be incorrect, and on principle, should | |
1408 | ;; be unnecessary too. -- rms. | |
1409 | (if flyspell-issue-message-flag | |
1410 | (message "Spell Checking...%d%% [%s]" | |
1411 | (* 100 (/ (float (point)) (point-max))) | |
1412 | word)) | |
1413 | (with-current-buffer flyspell-large-region-buffer | |
51978cac | 1414 | (goto-char buffer-scan-pos) |
27e0edcd EZ |
1415 | (let ((keep t)) |
1416 | ;; Iterate on string search until string is found as word, | |
1417 | ;; not as substring | |
1418 | (while keep | |
1419 | (if (search-forward word | |
1420 | flyspell-large-region-end t) | |
51978cac RS |
1421 | (let* ((found-list |
1422 | (save-excursion | |
1423 | ;; Move back into the match | |
1424 | ;; so flyspell-get-word will find it. | |
1425 | (forward-char -1) | |
f3e3a990 | 1426 | (flyspell-get-word))) |
51978cac RS |
1427 | (found (car found-list)) |
1428 | (found-length (length found)) | |
1429 | (misspell-length (length word))) | |
1430 | (when (or | |
1431 | ;; Size matches, we really found it. | |
1432 | (= found-length misspell-length) | |
1433 | ;; Matches as part of a boundary-char separated word | |
1434 | (member word | |
1435 | (split-string found ispell-otherchars)) | |
1436 | ;; Misspelling has higher length than | |
1437 | ;; what flyspell considers the | |
1438 | ;; word. Caused by boundary-chars | |
1439 | ;; mismatch. Validating seems safe. | |
1440 | (< found-length misspell-length) | |
1441 | ;; ispell treats beginning of some TeX | |
1442 | ;; commands as nroff control sequences | |
1443 | ;; and strips them in the list of | |
1444 | ;; misspelled words thus giving a | |
1445 | ;; non-existent word. Skip if ispell | |
1446 | ;; is used, string is a TeX command | |
1447 | ;; (char before beginning of word is | |
1448 | ;; backslash) and none of the previous | |
da6062e6 | 1449 | ;; conditions match. |
51978cac RS |
1450 | (and (not ispell-really-aspell) |
1451 | (save-excursion | |
1452 | (goto-char (- (nth 1 found-list) 1)) | |
1453 | (if (looking-at "[\\]" ) | |
1454 | t | |
1455 | nil)))) | |
1456 | (setq keep nil) | |
bd4532fc | 1457 | (flyspell-word nil t) |
51978cac RS |
1458 | ;; Search for next misspelled word will begin from |
1459 | ;; end of last validated match. | |
1460 | (setq buffer-scan-pos (point)))) | |
27e0edcd EZ |
1461 | ;; Record if misspelling is not found and try new one |
1462 | (add-to-list 'words-not-found | |
1463 | (concat " -> " word " - " | |
1464 | (int-to-string wordpos))) | |
1465 | (setq keep nil))))))) | |
1466 | ;; we are done | |
1467 | (if flyspell-issue-message-flag (message "Spell Checking completed."))) | |
1468 | ;; Warn about not found misspellings | |
1469 | (dolist (word words-not-found) | |
1470 | (message "%s: word not found" word)) | |
1471 | ;; Kill and forget the buffer with the list of incorrect words. | |
1472 | (kill-buffer flyspell-external-ispell-buffer) | |
1473 | (setq flyspell-external-ispell-buffer nil))) | |
2fced6f9 | 1474 | |
042c6fb7 SM |
1475 | ;;*---------------------------------------------------------------------*/ |
1476 | ;;* flyspell-process-localwords ... */ | |
1477 | ;;* ------------------------------------------------------------- */ | |
1478 | ;;* This function is used to prevent marking of words explicitly */ | |
1479 | ;;* declared correct. */ | |
1480 | ;;*---------------------------------------------------------------------*/ | |
b8b7c66e | 1481 | (defun flyspell-process-localwords (misspellings-buffer) |
da00640a AM |
1482 | (let ((localwords ispell-buffer-session-localwords) |
1483 | case-fold-search | |
b8b7c66e RS |
1484 | (ispell-casechars (ispell-get-casechars))) |
1485 | ;; Get localwords from the original buffer | |
1486 | (save-excursion | |
1487 | (goto-char (point-min)) | |
1488 | ;; Localwords parsing copied from ispell.el. | |
1489 | (while (search-forward ispell-words-keyword nil t) | |
e180ab9f | 1490 | (let ((end (point-at-eol)) |
b8b7c66e RS |
1491 | string) |
1492 | ;; buffer-local words separated by a space, and can contain | |
1493 | ;; any character other than a space. Not rigorous enough. | |
1494 | (while (re-search-forward " *\\([^ ]+\\)" end t) | |
1495 | (setq string (buffer-substring-no-properties (match-beginning 1) | |
1496 | (match-end 1))) | |
1497 | ;; This can fail when string contains a word with invalid chars. | |
1498 | ;; Error handling needs to be added between Ispell and Emacs. | |
27e0edcd | 1499 | (if (and (< 1 (length string)) |
b8b7c66e RS |
1500 | (equal 0 (string-match ispell-casechars string))) |
1501 | (push string localwords)))))) | |
1502 | ;; Remove localwords matches from misspellings-buffer. | |
1503 | ;; The usual mechanism of communicating the local words to ispell | |
1504 | ;; does not affect the special ispell process used by | |
1505 | ;; flyspell-large-region. | |
1506 | (with-current-buffer misspellings-buffer | |
1507 | (save-excursion | |
1508 | (dolist (word localwords) | |
1509 | (goto-char (point-min)) | |
1510 | (let ((regexp (concat "^" word "\n"))) | |
1511 | (while (re-search-forward regexp nil t) | |
1512 | (delete-region (match-beginning 0) (match-end 0))))))))) | |
1513 | ||
5c823193 CY |
1514 | ;;* --------------------------------------------------------------- |
1515 | ;;* flyspell-check-region-doublons | |
1516 | ;;* --------------------------------------------------------------- | |
1517 | (defun flyspell-check-region-doublons (beg end) | |
1518 | "Check for adjacent duplicated words (doublons) in the given region." | |
1519 | (save-excursion | |
1520 | (goto-char beg) | |
1521 | (flyspell-word) ; Make sure current word is checked | |
1522 | (backward-word 1) | |
1523 | (while (and (< (point) end) | |
081ff0c9 | 1524 | (re-search-forward "\\<\\(\\w+\\)\\>[ \n\t\f]+\\1\\>" |
5c823193 CY |
1525 | end 'move)) |
1526 | (flyspell-word) | |
1527 | (backward-word 1)) | |
1528 | (flyspell-word))) | |
1529 | ||
042c6fb7 SM |
1530 | ;;*---------------------------------------------------------------------*/ |
1531 | ;;* flyspell-large-region ... */ | |
1532 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
1533 | (defun flyspell-large-region (beg end) |
1534 | (let* ((curbuf (current-buffer)) | |
1535 | (buffer (get-buffer-create "*flyspell-region*"))) | |
1536 | (setq flyspell-external-ispell-buffer buffer) | |
1537 | (setq flyspell-large-region-buffer curbuf) | |
1538 | (setq flyspell-large-region-beg beg) | |
1539 | (setq flyspell-large-region-end end) | |
b8b7c66e | 1540 | (flyspell-accept-buffer-local-defs) |
3215afc4 GM |
1541 | (set-buffer buffer) |
1542 | (erase-buffer) | |
25f2ad05 | 1543 | ;; this is done, we can start checking... |
73194d67 | 1544 | (if flyspell-issue-message-flag (message "Checking region...")) |
3215afc4 | 1545 | (set-buffer curbuf) |
caea54f8 | 1546 | (ispell-set-spellchecker-params) ; Initialize variables and dicts alists |
a3614e04 GM |
1547 | ;; Local dictionary becomes the global dictionary in use. |
1548 | (setq ispell-current-dictionary | |
1549 | (or ispell-local-dictionary ispell-dictionary)) | |
1550 | (setq ispell-current-personal-dictionary | |
1551 | (or ispell-local-pdict ispell-personal-dictionary)) | |
1552 | (let ((args (ispell-get-ispell-args)) | |
1553 | (encoding (ispell-get-coding-system)) | |
1554 | c) | |
1555 | (if (and ispell-current-dictionary ; use specified dictionary | |
1556 | (not (member "-d" args))) ; only define if not overridden | |
1557 | (setq args | |
1558 | (append (list "-d" ispell-current-dictionary) args))) | |
1559 | (if ispell-current-personal-dictionary ; use specified pers dict | |
1560 | (setq args | |
1561 | (append args | |
1562 | (list "-p" | |
1563 | (expand-file-name | |
1564 | ispell-current-personal-dictionary))))) | |
8b7a997c AM |
1565 | |
1566 | ;; Check for extended character mode | |
1567 | (let ((extended-char-mode (ispell-get-extended-character-mode))) | |
1568 | (and extended-char-mode ; ~ extended character mode | |
1569 | (string-match "[^~]+$" extended-char-mode) | |
1570 | (add-to-list 'args (concat "-T" (match-string 0 extended-char-mode))))) | |
1571 | ||
1572 | ;; Add ispell-extra-args | |
a3614e04 | 1573 | (setq args (append args ispell-extra-args)) |
c478e4c5 AM |
1574 | |
1575 | ;; If we are using recent aspell or hunspell, make sure we use the right encoding | |
1576 | ;; for communication. ispell or older aspell/hunspell does not support this | |
57bf8fd4 | 1577 | (if ispell-encoding8-command |
a3614e04 GM |
1578 | (setq args |
1579 | (append args | |
64a440db AM |
1580 | (if ispell-really-hunspell |
1581 | (list ispell-encoding8-command | |
1582 | (upcase (symbol-name encoding))) | |
1583 | (list (concat ispell-encoding8-command | |
1584 | (symbol-name encoding))))))) | |
c478e4c5 | 1585 | |
a3614e04 GM |
1586 | (let ((process-coding-system-alist (list (cons "\\.*" encoding)))) |
1587 | (setq c (apply 'ispell-call-process-region beg | |
1588 | end | |
1589 | ispell-program-name | |
1590 | nil | |
1591 | buffer | |
1592 | nil | |
1593 | (if ispell-really-aspell "list" "-l") | |
1594 | args))) | |
15502042 | 1595 | (if (eq c 0) |
b8b7c66e RS |
1596 | (progn |
1597 | (flyspell-process-localwords buffer) | |
1598 | (with-current-buffer curbuf | |
5c823193 CY |
1599 | (flyspell-delete-region-overlays beg end) |
1600 | (flyspell-check-region-doublons beg end)) | |
b8b7c66e | 1601 | (flyspell-external-point-words)) |
c2e161b2 | 1602 | (error "Can't check region"))))) |
3215afc4 | 1603 | |
042c6fb7 SM |
1604 | ;;*---------------------------------------------------------------------*/ |
1605 | ;;* flyspell-region ... */ | |
1606 | ;;* ------------------------------------------------------------- */ | |
1607 | ;;* Because `ispell -a' is too slow, it is not possible to use */ | |
1608 | ;;* it on large region. Then, when ispell is invoked on a large */ | |
1609 | ;;* text region, a new `ispell -l' process is spawned. The */ | |
1610 | ;;* pointed out words are then searched in the region a checked with */ | |
1611 | ;;* regular flyspell means. */ | |
1612 | ;;*---------------------------------------------------------------------*/ | |
3bb710b0 | 1613 | ;;;###autoload |
3215afc4 GM |
1614 | (defun flyspell-region (beg end) |
1615 | "Flyspell text between BEG and END." | |
1616 | (interactive "r") | |
caea54f8 | 1617 | (ispell-set-spellchecker-params) ; Initialize variables and dicts alists |
3215afc4 GM |
1618 | (if (= beg end) |
1619 | () | |
1620 | (save-excursion | |
1621 | (if (> beg end) | |
1622 | (let ((old beg)) | |
1623 | (setq beg end) | |
1624 | (setq end old))) | |
a8c453e6 | 1625 | (if (and flyspell-large-region (> (- end beg) flyspell-large-region)) |
3215afc4 GM |
1626 | (flyspell-large-region beg end) |
1627 | (flyspell-small-region beg end))))) | |
1628 | ||
042c6fb7 SM |
1629 | ;;*---------------------------------------------------------------------*/ |
1630 | ;;* flyspell-buffer ... */ | |
1631 | ;;*---------------------------------------------------------------------*/ | |
3bb710b0 | 1632 | ;;;###autoload |
60371a2e RS |
1633 | (defun flyspell-buffer () |
1634 | "Flyspell whole buffer." | |
1635 | (interactive) | |
1636 | (flyspell-region (point-min) (point-max))) | |
1637 | ||
042c6fb7 SM |
1638 | ;;*---------------------------------------------------------------------*/ |
1639 | ;;* old next error position ... */ | |
1640 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
1641 | (defvar flyspell-old-buffer-error nil) |
1642 | (defvar flyspell-old-pos-error nil) | |
1643 | ||
042c6fb7 SM |
1644 | ;;*---------------------------------------------------------------------*/ |
1645 | ;;* flyspell-goto-next-error ... */ | |
1646 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
1647 | (defun flyspell-goto-next-error () |
1648 | "Go to the next previously detected error. | |
1649 | In general FLYSPELL-GOTO-NEXT-ERROR must be used after | |
1650 | FLYSPELL-BUFFER." | |
1651 | (interactive) | |
1652 | (let ((pos (point)) | |
1653 | (max (point-max))) | |
1654 | (if (and (eq (current-buffer) flyspell-old-buffer-error) | |
1655 | (eq pos flyspell-old-pos-error)) | |
1656 | (progn | |
1657 | (if (= flyspell-old-pos-error max) | |
1658 | ;; goto beginning of buffer | |
1659 | (progn | |
1660 | (message "Restarting from beginning of buffer") | |
1661 | (goto-char (point-min))) | |
1662 | (forward-word 1)) | |
1663 | (setq pos (point)))) | |
1664 | ;; seek the next error | |
1665 | (while (and (< pos max) | |
1666 | (let ((ovs (overlays-at pos)) | |
1667 | (r '())) | |
1668 | (while (and (not r) (consp ovs)) | |
1669 | (if (flyspell-overlay-p (car ovs)) | |
1670 | (setq r t) | |
1671 | (setq ovs (cdr ovs)))) | |
1672 | (not r))) | |
1673 | (setq pos (1+ pos))) | |
25f2ad05 | 1674 | ;; save the current location for next invocation |
3215afc4 GM |
1675 | (setq flyspell-old-pos-error pos) |
1676 | (setq flyspell-old-buffer-error (current-buffer)) | |
1677 | (goto-char pos) | |
1678 | (if (= pos max) | |
1679 | (message "No more miss-spelled word!")))) | |
1680 | ||
042c6fb7 SM |
1681 | ;;*---------------------------------------------------------------------*/ |
1682 | ;;* flyspell-overlay-p ... */ | |
1683 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 1684 | (defun flyspell-overlay-p (o) |
3ecd3a56 | 1685 | "Return true if O is an overlay used by flyspell." |
60371a2e RS |
1686 | (and (overlayp o) (overlay-get o 'flyspell-overlay))) |
1687 | ||
042c6fb7 SM |
1688 | ;;*---------------------------------------------------------------------*/ |
1689 | ;;* flyspell-delete-region-overlays, flyspell-delete-all-overlays */ | |
1690 | ;;* ------------------------------------------------------------- */ | |
1691 | ;;* Remove overlays introduced by flyspell. */ | |
1692 | ;;*---------------------------------------------------------------------*/ | |
b8b7c66e RS |
1693 | (defun flyspell-delete-region-overlays (beg end) |
1694 | "Delete overlays used by flyspell in a given region." | |
5cb35db5 | 1695 | (remove-overlays beg end 'flyspell-overlay t)) |
60371a2e | 1696 | |
b8b7c66e RS |
1697 | |
1698 | (defun flyspell-delete-all-overlays () | |
1699 | "Delete all the overlays used by flyspell." | |
324d09a6 | 1700 | (remove-overlays (point-min) (point-max) 'flyspell-overlay t)) |
b8b7c66e | 1701 | |
042c6fb7 SM |
1702 | ;;*---------------------------------------------------------------------*/ |
1703 | ;;* flyspell-unhighlight-at ... */ | |
1704 | ;;*---------------------------------------------------------------------*/ | |
60371a2e RS |
1705 | (defun flyspell-unhighlight-at (pos) |
1706 | "Remove the flyspell overlay that are located at POS." | |
1707 | (if flyspell-persistent-highlight | |
1708 | (let ((overlays (overlays-at pos))) | |
1709 | (while (consp overlays) | |
1710 | (if (flyspell-overlay-p (car overlays)) | |
1711 | (delete-overlay (car overlays))) | |
1712 | (setq overlays (cdr overlays)))) | |
3215afc4 | 1713 | (if (flyspell-overlay-p flyspell-overlay) |
042c6fb7 | 1714 | (delete-overlay flyspell-overlay)))) |
60371a2e | 1715 | |
042c6fb7 SM |
1716 | ;;*---------------------------------------------------------------------*/ |
1717 | ;;* flyspell-properties-at-p ... */ | |
1718 | ;;* ------------------------------------------------------------- */ | |
1719 | ;;* Is there an highlight properties at position pos? */ | |
1720 | ;;*---------------------------------------------------------------------*/ | |
65a5c06a RS |
1721 | (defun flyspell-properties-at-p (pos) |
1722 | "Return t if there is a text property at POS, not counting `local-map'. | |
1723 | If variable `flyspell-highlight-properties' is set to nil, | |
1724 | text with properties are not checked. This function is used to discover | |
1725 | if the character at POS has any other property." | |
1726 | (let ((prop (text-properties-at pos)) | |
60371a2e RS |
1727 | (keep t)) |
1728 | (while (and keep (consp prop)) | |
1729 | (if (and (eq (car prop) 'local-map) (consp (cdr prop))) | |
1730 | (setq prop (cdr (cdr prop))) | |
1731 | (setq keep nil))) | |
1732 | (consp prop))) | |
1733 | ||
042c6fb7 SM |
1734 | ;;*---------------------------------------------------------------------*/ |
1735 | ;;* make-flyspell-overlay ... */ | |
1736 | ;;*---------------------------------------------------------------------*/ | |
60371a2e | 1737 | (defun make-flyspell-overlay (beg end face mouse-face) |
0a67052f RS |
1738 | "Allocate an overlay to highlight an incorrect word. |
1739 | BEG and END specify the range in the buffer of that word. | |
1740 | FACE and MOUSE-FACE specify the `face' and `mouse-face' properties | |
1741 | for the overlay." | |
042c6fb7 SM |
1742 | (let ((overlay (make-overlay beg end nil t nil))) |
1743 | (overlay-put overlay 'face face) | |
1744 | (overlay-put overlay 'mouse-face mouse-face) | |
1745 | (overlay-put overlay 'flyspell-overlay t) | |
1746 | (overlay-put overlay 'evaporate t) | |
1747 | (overlay-put overlay 'help-echo "mouse-2: correct word at point") | |
1748 | (overlay-put overlay 'keymap flyspell-mouse-map) | |
c43aed5a | 1749 | (when (eq face 'flyspell-incorrect) |
73194d67 | 1750 | (and (stringp flyspell-before-incorrect-word-string) |
042c6fb7 | 1751 | (overlay-put overlay 'before-string |
73194d67 PJ |
1752 | flyspell-before-incorrect-word-string)) |
1753 | (and (stringp flyspell-after-incorrect-word-string) | |
042c6fb7 | 1754 | (overlay-put overlay 'after-string |
73194d67 | 1755 | flyspell-after-incorrect-word-string))) |
042c6fb7 | 1756 | overlay)) |
2fced6f9 | 1757 | |
042c6fb7 SM |
1758 | ;;*---------------------------------------------------------------------*/ |
1759 | ;;* flyspell-highlight-incorrect-region ... */ | |
1760 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 | 1761 | (defun flyspell-highlight-incorrect-region (beg end poss) |
91346f54 RS |
1762 | "Set up an overlay on a misspelled word, in the buffer from BEG to END. |
1763 | POSS is usually a list of possible spelling/correction lists, | |
1764 | as returned by `ispell-parse-output'. | |
1765 | It can also be the symbol `doublon', in the case where the word | |
1766 | is itself incorrect, but suspiciously repeated." | |
4c685fb8 JW |
1767 | (let ((inhibit-read-only t)) |
1768 | (unless (run-hook-with-args-until-success | |
1769 | 'flyspell-incorrect-hook beg end poss) | |
1770 | (if (or flyspell-highlight-properties | |
1771 | (not (flyspell-properties-at-p beg))) | |
1772 | (progn | |
a8c453e6 RS |
1773 | ;; we cleanup all the overlay that are in the region, not |
1774 | ;; beginning at the word start position | |
1775 | (if (< (1+ beg) end) | |
1776 | (let ((os (overlays-in (1+ beg) end))) | |
1777 | (while (consp os) | |
1778 | (if (flyspell-overlay-p (car os)) | |
1779 | (delete-overlay (car os))) | |
1780 | (setq os (cdr os))))) | |
4c685fb8 | 1781 | ;; we cleanup current overlay at the same position |
042c6fb7 | 1782 | (flyspell-unhighlight-at beg) |
4c685fb8 JW |
1783 | ;; now we can use a new overlay |
1784 | (setq flyspell-overlay | |
1785 | (make-flyspell-overlay | |
6b8aed24 CY |
1786 | beg end |
1787 | (if (eq poss 'doublon) 'flyspell-duplicate 'flyspell-incorrect) | |
1788 | 'highlight))))))) | |
60371a2e | 1789 | |
042c6fb7 SM |
1790 | ;;*---------------------------------------------------------------------*/ |
1791 | ;;* flyspell-highlight-duplicate-region ... */ | |
1792 | ;;*---------------------------------------------------------------------*/ | |
5106fd70 | 1793 | (defun flyspell-highlight-duplicate-region (beg end poss) |
91346f54 RS |
1794 | "Set up an overlay on a duplicate misspelled word, in the buffer from BEG to END. |
1795 | POSS is a list of possible spelling/correction lists, | |
1796 | as returned by `ispell-parse-output'." | |
4c685fb8 JW |
1797 | (let ((inhibit-read-only t)) |
1798 | (unless (run-hook-with-args-until-success | |
1799 | 'flyspell-incorrect-hook beg end poss) | |
1800 | (if (or flyspell-highlight-properties | |
1801 | (not (flyspell-properties-at-p beg))) | |
1802 | (progn | |
1803 | ;; we cleanup current overlay at the same position | |
042c6fb7 | 1804 | (flyspell-unhighlight-at beg) |
4c685fb8 JW |
1805 | ;; now we can use a new overlay |
1806 | (setq flyspell-overlay | |
1807 | (make-flyspell-overlay beg end | |
c43aed5a | 1808 | 'flyspell-duplicate |
4c685fb8 | 1809 | 'highlight))))))) |
60371a2e | 1810 | |
042c6fb7 SM |
1811 | ;;*---------------------------------------------------------------------*/ |
1812 | ;;* flyspell-auto-correct-cache ... */ | |
1813 | ;;*---------------------------------------------------------------------*/ | |
60371a2e RS |
1814 | (defvar flyspell-auto-correct-pos nil) |
1815 | (defvar flyspell-auto-correct-region nil) | |
1816 | (defvar flyspell-auto-correct-ring nil) | |
3215afc4 GM |
1817 | (defvar flyspell-auto-correct-word nil) |
1818 | (make-variable-buffer-local 'flyspell-auto-correct-pos) | |
1819 | (make-variable-buffer-local 'flyspell-auto-correct-region) | |
1820 | (make-variable-buffer-local 'flyspell-auto-correct-ring) | |
1821 | (make-variable-buffer-local 'flyspell-auto-correct-word) | |
60371a2e | 1822 | |
042c6fb7 SM |
1823 | ;;*---------------------------------------------------------------------*/ |
1824 | ;;* flyspell-check-previous-highlighted-word ... */ | |
1825 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 | 1826 | (defun flyspell-check-previous-highlighted-word (&optional arg) |
25f2ad05 | 1827 | "Correct the closer misspelled word. |
3215afc4 GM |
1828 | This function scans a mis-spelled word before the cursor. If it finds one |
1829 | it proposes replacement for that word. With prefix arg, count that many | |
1830 | misspelled words backwards." | |
1831 | (interactive) | |
1832 | (let ((pos1 (point)) | |
1833 | (pos (point)) | |
1834 | (arg (if (or (not (numberp arg)) (< arg 1)) 1 arg)) | |
1835 | ov ovs) | |
1836 | (if (catch 'exit | |
1837 | (while (and (setq pos (previous-overlay-change pos)) | |
1838 | (not (= pos pos1))) | |
1839 | (setq pos1 pos) | |
1840 | (if (> pos (point-min)) | |
1841 | (progn | |
1842 | (setq ovs (overlays-at (1- pos))) | |
1843 | (while (consp ovs) | |
1844 | (setq ov (car ovs)) | |
1845 | (setq ovs (cdr ovs)) | |
042c6fb7 | 1846 | (if (and (flyspell-overlay-p ov) |
3215afc4 GM |
1847 | (= 0 (setq arg (1- arg)))) |
1848 | (throw 'exit t))))))) | |
60371a2e | 1849 | (save-excursion |
3215afc4 | 1850 | (goto-char pos) |
c11a5a9c AM |
1851 | (ispell-word) |
1852 | (setq flyspell-word-cache-word nil) ;; Force flyspell-word re-check | |
1853 | (flyspell-word)) | |
1f44857f | 1854 | (error "No word to correct before point")))) |
3215afc4 | 1855 | |
042c6fb7 SM |
1856 | ;;*---------------------------------------------------------------------*/ |
1857 | ;;* flyspell-display-next-corrections ... */ | |
1858 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
1859 | (defun flyspell-display-next-corrections (corrections) |
1860 | (let ((string "Corrections:") | |
1861 | (l corrections) | |
1862 | (pos '())) | |
1863 | (while (< (length string) 80) | |
1864 | (if (equal (car l) flyspell-auto-correct-word) | |
1865 | (setq pos (cons (+ 1 (length string)) pos))) | |
1866 | (setq string (concat string " " (car l))) | |
1867 | (setq l (cdr l))) | |
1868 | (while (consp pos) | |
1869 | (let ((num (car pos))) | |
1870 | (put-text-property num | |
1871 | (+ num (length flyspell-auto-correct-word)) | |
c43aed5a | 1872 | 'face 'flyspell-incorrect |
3215afc4 GM |
1873 | string)) |
1874 | (setq pos (cdr pos))) | |
1875 | (if (fboundp 'display-message) | |
1876 | (display-message 'no-log string) | |
5673af85 | 1877 | (message "%s" string)))) |
3215afc4 | 1878 | |
042c6fb7 SM |
1879 | ;;*---------------------------------------------------------------------*/ |
1880 | ;;* flyspell-abbrev-table ... */ | |
1881 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
1882 | (defun flyspell-abbrev-table () |
1883 | (if flyspell-use-global-abbrev-table-p | |
1884 | global-abbrev-table | |
67d120eb | 1885 | (or local-abbrev-table global-abbrev-table))) |
3215afc4 | 1886 | |
042c6fb7 SM |
1887 | ;;*---------------------------------------------------------------------*/ |
1888 | ;;* flyspell-define-abbrev ... */ | |
1889 | ;;*---------------------------------------------------------------------*/ | |
73194d67 PJ |
1890 | (defun flyspell-define-abbrev (name expansion) |
1891 | (let ((table (flyspell-abbrev-table))) | |
1892 | (when table | |
2aebf08d | 1893 | (define-abbrev table (downcase name) expansion)))) |
73194d67 | 1894 | |
042c6fb7 SM |
1895 | ;;*---------------------------------------------------------------------*/ |
1896 | ;;* flyspell-auto-correct-word ... */ | |
1897 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
1898 | (defun flyspell-auto-correct-word () |
1899 | "Correct the current word. | |
1900 | This command proposes various successive corrections for the current word." | |
1901 | (interactive) | |
1902 | (let ((pos (point)) | |
1903 | (old-max (point-max))) | |
1904 | ;; use the correct dictionary | |
1905 | (flyspell-accept-buffer-local-defs) | |
1906 | (if (and (eq flyspell-auto-correct-pos pos) | |
1907 | (consp flyspell-auto-correct-region)) | |
1908 | ;; we have already been using the function at the same location | |
1909 | (let* ((start (car flyspell-auto-correct-region)) | |
1910 | (len (cdr flyspell-auto-correct-region))) | |
73194d67 | 1911 | (flyspell-unhighlight-at start) |
3215afc4 GM |
1912 | (delete-region start (+ start len)) |
1913 | (setq flyspell-auto-correct-ring (cdr flyspell-auto-correct-ring)) | |
1914 | (let* ((word (car flyspell-auto-correct-ring)) | |
1915 | (len (length word))) | |
1916 | (rplacd flyspell-auto-correct-region len) | |
1917 | (goto-char start) | |
1918 | (if flyspell-abbrev-p | |
1919 | (if (flyspell-already-abbrevp (flyspell-abbrev-table) | |
1920 | flyspell-auto-correct-word) | |
1921 | (flyspell-change-abbrev (flyspell-abbrev-table) | |
1922 | flyspell-auto-correct-word | |
1923 | word) | |
73194d67 PJ |
1924 | (flyspell-define-abbrev flyspell-auto-correct-word word))) |
1925 | (funcall flyspell-insert-function word) | |
3215afc4 GM |
1926 | (flyspell-word) |
1927 | (flyspell-display-next-corrections flyspell-auto-correct-ring)) | |
1928 | (flyspell-ajust-cursor-point pos (point) old-max) | |
1929 | (setq flyspell-auto-correct-pos (point))) | |
1930 | ;; fetch the word to be checked | |
f3e3a990 | 1931 | (let ((word (flyspell-get-word))) |
a8c453e6 RS |
1932 | (if (consp word) |
1933 | (let ((start (car (cdr word))) | |
1934 | (end (car (cdr (cdr word)))) | |
1935 | (word (car word)) | |
9c0fad71 | 1936 | poss ispell-filter) |
a8c453e6 RS |
1937 | (setq flyspell-auto-correct-word word) |
1938 | ;; now check spelling of word. | |
042c6fb7 SM |
1939 | (ispell-send-string "%\n") ;put in verbose mode |
1940 | (ispell-send-string (concat "^" word "\n")) | |
1941 | ;; wait until ispell has processed word. | |
1942 | (while (progn | |
1943 | (accept-process-output ispell-process) | |
1944 | (not (string= "" (car ispell-filter))))) | |
9c0fad71 | 1945 | ;; Remove leading empty element |
a8c453e6 | 1946 | (setq ispell-filter (cdr ispell-filter)) |
9c0fad71 RS |
1947 | ;; ispell process should return something after word is sent. |
1948 | ;; Tag word as valid (i.e., skip) otherwise | |
1949 | (or ispell-filter | |
1950 | (setq ispell-filter '(*))) | |
a8c453e6 RS |
1951 | (if (consp ispell-filter) |
1952 | (setq poss (ispell-parse-output (car ispell-filter)))) | |
1953 | (cond | |
1954 | ((or (eq poss t) (stringp poss)) | |
1955 | ;; don't correct word | |
1956 | t) | |
1957 | ((null poss) | |
1958 | ;; ispell error | |
1959 | (error "Ispell: error in Ispell process")) | |
1960 | (t | |
1961 | ;; the word is incorrect, we have to propose a replacement | |
1962 | (let ((replacements (if flyspell-sort-corrections | |
1963 | (sort (car (cdr (cdr poss))) 'string<) | |
1964 | (car (cdr (cdr poss)))))) | |
1965 | (setq flyspell-auto-correct-region nil) | |
1966 | (if (consp replacements) | |
1967 | (progn | |
1968 | (let ((replace (car replacements))) | |
1969 | (let ((new-word replace)) | |
1970 | (if (not (equal new-word (car poss))) | |
1971 | (progn | |
1972 | ;; the save the current replacements | |
1973 | (setq flyspell-auto-correct-region | |
1974 | (cons start (length new-word))) | |
1975 | (let ((l replacements)) | |
1976 | (while (consp (cdr l)) | |
1977 | (setq l (cdr l))) | |
1978 | (rplacd l (cons (car poss) replacements))) | |
1979 | (setq flyspell-auto-correct-ring | |
1980 | replacements) | |
1981 | (flyspell-unhighlight-at start) | |
1982 | (delete-region start end) | |
1983 | (funcall flyspell-insert-function new-word) | |
1984 | (if flyspell-abbrev-p | |
1985 | (if (flyspell-already-abbrevp | |
1986 | (flyspell-abbrev-table) word) | |
1987 | (flyspell-change-abbrev | |
1988 | (flyspell-abbrev-table) | |
1989 | word | |
1990 | new-word) | |
1991 | (flyspell-define-abbrev word | |
1992 | new-word))) | |
1993 | (flyspell-word) | |
1994 | (flyspell-display-next-corrections | |
1995 | (cons new-word flyspell-auto-correct-ring)) | |
1996 | (flyspell-ajust-cursor-point pos | |
1997 | (point) | |
1998 | old-max)))))))))) | |
1999 | (setq flyspell-auto-correct-pos (point)) | |
2000 | (ispell-pdict-save t))))))) | |
2fced6f9 | 2001 | |
042c6fb7 SM |
2002 | ;;*---------------------------------------------------------------------*/ |
2003 | ;;* flyspell-auto-correct-previous-pos ... */ | |
2004 | ;;*---------------------------------------------------------------------*/ | |
73194d67 PJ |
2005 | (defvar flyspell-auto-correct-previous-pos nil |
2006 | "Holds the start of the first incorrect word before point.") | |
2007 | ||
042c6fb7 SM |
2008 | ;;*---------------------------------------------------------------------*/ |
2009 | ;;* flyspell-auto-correct-previous-hook ... */ | |
2010 | ;;*---------------------------------------------------------------------*/ | |
73194d67 PJ |
2011 | (defun flyspell-auto-correct-previous-hook () |
2012 | "Hook to track successive calls to `flyspell-auto-correct-previous-word'. | |
a8c453e6 | 2013 | Sets `flyspell-auto-correct-previous-pos' to nil" |
ebb7de84 | 2014 | (interactive) |
73194d67 PJ |
2015 | (remove-hook 'pre-command-hook (function flyspell-auto-correct-previous-hook) t) |
2016 | (unless (eq this-command (function flyspell-auto-correct-previous-word)) | |
2017 | (setq flyspell-auto-correct-previous-pos nil))) | |
2018 | ||
042c6fb7 SM |
2019 | ;;*---------------------------------------------------------------------*/ |
2020 | ;;* flyspell-auto-correct-previous-word ... */ | |
2021 | ;;*---------------------------------------------------------------------*/ | |
ebb7de84 | 2022 | (defun flyspell-auto-correct-previous-word (position) |
c2e161b2 | 2023 | "Auto correct the first misspelled word that occurs before point. |
a8c453e6 | 2024 | But don't look beyond what's visible on the screen." |
73194d67 PJ |
2025 | (interactive "d") |
2026 | ||
30133f6d CY |
2027 | (let ((top (window-start)) |
2028 | (bot (window-end))) | |
a8c453e6 RS |
2029 | (save-excursion |
2030 | (save-restriction | |
2031 | (narrow-to-region top bot) | |
a8c453e6 RS |
2032 | (overlay-recenter (point)) |
2033 | ||
ebb7de84 | 2034 | (add-hook 'pre-command-hook |
a8c453e6 RS |
2035 | (function flyspell-auto-correct-previous-hook) t t) |
2036 | ||
2037 | (unless flyspell-auto-correct-previous-pos | |
2038 | ;; only reset if a new overlay exists | |
2039 | (setq flyspell-auto-correct-previous-pos nil) | |
ebb7de84 | 2040 | |
a8c453e6 RS |
2041 | (let ((overlay-list (overlays-in (point-min) position)) |
2042 | (new-overlay 'dummy-value)) | |
ebb7de84 | 2043 | |
a8c453e6 RS |
2044 | ;; search for previous (new) flyspell overlay |
2045 | (while (and new-overlay | |
2046 | (or (not (flyspell-overlay-p new-overlay)) | |
2047 | ;; check if its face has changed | |
ebb7de84 JB |
2048 | (not (eq (get-char-property |
2049 | (overlay-start new-overlay) 'face) | |
c43aed5a | 2050 | 'flyspell-incorrect)))) |
a8c453e6 RS |
2051 | (setq new-overlay (car-safe overlay-list)) |
2052 | (setq overlay-list (cdr-safe overlay-list))) | |
ebb7de84 | 2053 | |
a8c453e6 RS |
2054 | ;; if nothing new exits new-overlay should be nil |
2055 | (if new-overlay ;; the length of the word may change so go to the start | |
ebb7de84 | 2056 | (setq flyspell-auto-correct-previous-pos |
a8c453e6 RS |
2057 | (overlay-start new-overlay))))) |
2058 | ||
2059 | (when flyspell-auto-correct-previous-pos | |
2060 | (save-excursion | |
2061 | (goto-char flyspell-auto-correct-previous-pos) | |
2062 | (let ((ispell-following-word t)) ;; point is at start | |
2063 | (if (numberp flyspell-auto-correct-previous-pos) | |
2064 | (goto-char flyspell-auto-correct-previous-pos)) | |
2065 | (flyspell-auto-correct-word)) | |
2066 | ;; the point may have moved so reset this | |
2067 | (setq flyspell-auto-correct-previous-pos (point)))))))) | |
73194d67 | 2068 | |
042c6fb7 SM |
2069 | ;;*---------------------------------------------------------------------*/ |
2070 | ;;* flyspell-correct-word ... */ | |
2071 | ;;*---------------------------------------------------------------------*/ | |
4071cac7 | 2072 | |
60371a2e | 2073 | (defun flyspell-correct-word (event) |
0a67052f RS |
2074 | "Pop up a menu of possible corrections for a misspelled word. |
2075 | The word checked is the word at the mouse position." | |
60371a2e | 2076 | (interactive "e") |
60371a2e RS |
2077 | (let ((save (point))) |
2078 | (mouse-set-point event) | |
4071cac7 RS |
2079 | (flyspell-correct-word-before-point event save))) |
2080 | ||
2081 | (defun flyspell-correct-word-before-point (&optional event opoint) | |
2082 | "Pop up a menu of possible corrections for misspelled word before point. | |
2083 | If EVENT is non-nil, it is the mouse event that invoked this operation; | |
2084 | that controls where to put the menu. | |
2085 | If OPOINT is non-nil, restore point there after adjusting it for replacement." | |
2086 | (interactive) | |
2087 | (unless (mouse-position) | |
2088 | (error "Pop-up menus do not work on this terminal")) | |
2089 | ;; use the correct dictionary | |
2090 | (flyspell-accept-buffer-local-defs) | |
873f4645 | 2091 | (or opoint (setq opoint (point))) |
4071cac7 | 2092 | (let ((cursor-location (point)) |
f3e3a990 | 2093 | (word (flyspell-get-word))) |
4071cac7 RS |
2094 | (if (consp word) |
2095 | (let ((start (car (cdr word))) | |
2096 | (end (car (cdr (cdr word)))) | |
2097 | (word (car word)) | |
2098 | poss ispell-filter) | |
2099 | ;; now check spelling of word. | |
2100 | (ispell-send-string "%\n") ;put in verbose mode | |
2101 | (ispell-send-string (concat "^" word "\n")) | |
2102 | ;; wait until ispell has processed word | |
2103 | (while (progn | |
2104 | (accept-process-output ispell-process) | |
2105 | (not (string= "" (car ispell-filter))))) | |
2106 | ;; Remove leading empty element | |
2107 | (setq ispell-filter (cdr ispell-filter)) | |
2108 | ;; ispell process should return something after word is sent. | |
2109 | ;; Tag word as valid (i.e., skip) otherwise | |
2110 | (or ispell-filter | |
2111 | (setq ispell-filter '(*))) | |
2112 | (if (consp ispell-filter) | |
2113 | (setq poss (ispell-parse-output (car ispell-filter)))) | |
2114 | (cond | |
2115 | ((or (eq poss t) (stringp poss)) | |
2116 | ;; don't correct word | |
2117 | t) | |
2118 | ((null poss) | |
2119 | ;; ispell error | |
2120 | (error "Ispell: error in Ispell process")) | |
2121 | ((featurep 'xemacs) | |
2122 | (flyspell-xemacs-popup | |
2123 | poss word cursor-location start end opoint)) | |
2124 | (t | |
2125 | ;; The word is incorrect, we have to propose a replacement. | |
2126 | (flyspell-do-correct (flyspell-emacs-popup event poss word) | |
2127 | poss word cursor-location start end opoint))) | |
2128 | (ispell-pdict-save t))))) | |
60371a2e | 2129 | |
042c6fb7 SM |
2130 | ;;*---------------------------------------------------------------------*/ |
2131 | ;;* flyspell-do-correct ... */ | |
2132 | ;;*---------------------------------------------------------------------*/ | |
7ad04640 SM |
2133 | (defun flyspell-do-correct (replace poss word cursor-location start end save) |
2134 | "The popup menu callback." | |
2135 | ;; Originally, the XEmacs code didn't do the (goto-char save) here and did | |
2136 | ;; it instead right after calling the function. | |
60371a2e | 2137 | (cond ((eq replace 'ignore) |
7ad04640 | 2138 | (goto-char save) |
60371a2e RS |
2139 | nil) |
2140 | ((eq replace 'save) | |
7ad04640 SM |
2141 | (goto-char save) |
2142 | (ispell-send-string (concat "*" word "\n")) | |
2143 | ;; This was added only to the XEmacs side in revision 1.18 of | |
2144 | ;; flyspell. I assume its absence on the Emacs side was an | |
2145 | ;; oversight. --Stef | |
2146 | (ispell-send-string "#\n") | |
60371a2e RS |
2147 | (flyspell-unhighlight-at cursor-location) |
2148 | (setq ispell-pdict-modified-p '(t))) | |
2149 | ((or (eq replace 'buffer) (eq replace 'session)) | |
7ad04640 | 2150 | (ispell-send-string (concat "@" word "\n")) |
da00640a AM |
2151 | (add-to-list 'ispell-buffer-session-localwords word) |
2152 | (or ispell-buffer-local-name ; session localwords might conflict | |
2153 | (setq ispell-buffer-local-name (buffer-name))) | |
60371a2e RS |
2154 | (flyspell-unhighlight-at cursor-location) |
2155 | (if (null ispell-pdict-modified-p) | |
2156 | (setq ispell-pdict-modified-p | |
2157 | (list ispell-pdict-modified-p))) | |
7ad04640 | 2158 | (goto-char save) |
60371a2e RS |
2159 | (if (eq replace 'buffer) |
2160 | (ispell-add-per-file-word-list word))) | |
2161 | (replace | |
7ad04640 SM |
2162 | ;; This was added only to the Emacs side. I assume its absence on |
2163 | ;; the XEmacs side was an oversight. --Stef | |
2164 | (flyspell-unhighlight-at cursor-location) | |
3215afc4 GM |
2165 | (let ((old-max (point-max)) |
2166 | (new-word (if (atom replace) | |
2167 | replace | |
2168 | (car replace))) | |
2169 | (cursor-location (+ (- (length word) (- end start)) | |
2170 | cursor-location))) | |
7ad04640 SM |
2171 | (unless (equal new-word (car poss)) |
2172 | (delete-region start end) | |
2173 | (goto-char start) | |
2174 | (funcall flyspell-insert-function new-word) | |
2175 | (if flyspell-abbrev-p | |
2176 | (flyspell-define-abbrev word new-word))) | |
2177 | ;; In the original Emacs code, this was only called in the body | |
2178 | ;; of the if. I arbitrarily kept the XEmacs behavior instead. | |
2179 | (flyspell-ajust-cursor-point save cursor-location old-max))) | |
2180 | (t | |
2181 | (goto-char save) | |
2182 | nil))) | |
3215afc4 | 2183 | |
042c6fb7 SM |
2184 | ;;*---------------------------------------------------------------------*/ |
2185 | ;;* flyspell-ajust-cursor-point ... */ | |
2186 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
2187 | (defun flyspell-ajust-cursor-point (save cursor-location old-max) |
2188 | (if (>= save cursor-location) | |
2189 | (let ((new-pos (+ save (- (point-max) old-max)))) | |
2190 | (goto-char (cond | |
2191 | ((< new-pos (point-min)) | |
2192 | (point-min)) | |
2193 | ((> new-pos (point-max)) | |
2194 | (point-max)) | |
2195 | (t new-pos)))) | |
2196 | (goto-char save))) | |
60371a2e | 2197 | |
042c6fb7 SM |
2198 | ;;*---------------------------------------------------------------------*/ |
2199 | ;;* flyspell-emacs-popup ... */ | |
2200 | ;;*---------------------------------------------------------------------*/ | |
0a67052f RS |
2201 | (defun flyspell-emacs-popup (event poss word) |
2202 | "The Emacs popup menu." | |
913a8cda RS |
2203 | (unless window-system |
2204 | (error "This command requires pop-up dialogs")) | |
60371a2e RS |
2205 | (if (not event) |
2206 | (let* ((mouse-pos (mouse-position)) | |
2207 | (mouse-pos (if (nth 1 mouse-pos) | |
2208 | mouse-pos | |
2209 | (set-mouse-position (car mouse-pos) | |
3215afc4 | 2210 | (/ (frame-width) 2) 2) |
60371a2e RS |
2211 | (mouse-position)))) |
2212 | (setq event (list (list (car (cdr mouse-pos)) | |
2213 | (1+ (cdr (cdr mouse-pos)))) | |
2214 | (car mouse-pos))))) | |
2215 | (let* ((corrects (if flyspell-sort-corrections | |
2216 | (sort (car (cdr (cdr poss))) 'string<) | |
2217 | (car (cdr (cdr poss))))) | |
2218 | (cor-menu (if (consp corrects) | |
2219 | (mapcar (lambda (correct) | |
2220 | (list correct correct)) | |
2221 | corrects) | |
2222 | '())) | |
2223 | (affix (car (cdr (cdr (cdr poss))))) | |
f60117ac EZ |
2224 | show-affix-info |
2225 | (base-menu (let ((save (if (and (consp affix) show-affix-info) | |
60371a2e RS |
2226 | (list |
2227 | (list (concat "Save affix: " (car affix)) | |
2228 | 'save) | |
73194d67 | 2229 | '("Accept (session)" session) |
60371a2e RS |
2230 | '("Accept (buffer)" buffer)) |
2231 | '(("Save word" save) | |
2232 | ("Accept (session)" session) | |
2233 | ("Accept (buffer)" buffer))))) | |
2234 | (if (consp cor-menu) | |
2235 | (append cor-menu (cons "" save)) | |
2236 | save))) | |
2237 | (menu (cons "flyspell correction menu" base-menu))) | |
2238 | (car (x-popup-menu event | |
2239 | (list (format "%s [%s]" word (or ispell-local-dictionary | |
2240 | ispell-dictionary)) | |
2241 | menu))))) | |
2242 | ||
042c6fb7 SM |
2243 | ;;*---------------------------------------------------------------------*/ |
2244 | ;;* flyspell-xemacs-popup ... */ | |
2245 | ;;*---------------------------------------------------------------------*/ | |
2246 | (defun flyspell-xemacs-popup (poss word cursor-location start end save) | |
1f44857f | 2247 | "The XEmacs popup menu." |
60371a2e RS |
2248 | (let* ((corrects (if flyspell-sort-corrections |
2249 | (sort (car (cdr (cdr poss))) 'string<) | |
2250 | (car (cdr (cdr poss))))) | |
2251 | (cor-menu (if (consp corrects) | |
2252 | (mapcar (lambda (correct) | |
2253 | (vector correct | |
7ad04640 | 2254 | (list 'flyspell-do-correct |
60371a2e RS |
2255 | correct |
2256 | (list 'quote poss) | |
2257 | word | |
2258 | cursor-location | |
2259 | start | |
3215afc4 GM |
2260 | end |
2261 | save) | |
60371a2e RS |
2262 | t)) |
2263 | corrects) | |
2264 | '())) | |
2265 | (affix (car (cdr (cdr (cdr poss))))) | |
f60117ac EZ |
2266 | show-affix-info |
2267 | (menu (let ((save (if (and (consp affix) show-affix-info) | |
60371a2e RS |
2268 | (vector |
2269 | (concat "Save affix: " (car affix)) | |
7ad04640 | 2270 | (list 'flyspell-do-correct |
60371a2e RS |
2271 | ''save |
2272 | (list 'quote poss) | |
2273 | word | |
2274 | cursor-location | |
2275 | start | |
3215afc4 GM |
2276 | end |
2277 | save) | |
60371a2e RS |
2278 | t) |
2279 | (vector | |
2280 | "Save word" | |
7ad04640 | 2281 | (list 'flyspell-do-correct |
60371a2e RS |
2282 | ''save |
2283 | (list 'quote poss) | |
2284 | word | |
2285 | cursor-location | |
2286 | start | |
3215afc4 GM |
2287 | end |
2288 | save) | |
60371a2e RS |
2289 | t))) |
2290 | (session (vector "Accept (session)" | |
7ad04640 | 2291 | (list 'flyspell-do-correct |
60371a2e RS |
2292 | ''session |
2293 | (list 'quote poss) | |
2294 | word | |
2295 | cursor-location | |
2296 | start | |
3215afc4 GM |
2297 | end |
2298 | save) | |
60371a2e RS |
2299 | t)) |
2300 | (buffer (vector "Accept (buffer)" | |
7ad04640 | 2301 | (list 'flyspell-do-correct |
60371a2e RS |
2302 | ''buffer |
2303 | (list 'quote poss) | |
2304 | word | |
2305 | cursor-location | |
2306 | start | |
3215afc4 GM |
2307 | end |
2308 | save) | |
60371a2e RS |
2309 | t))) |
2310 | (if (consp cor-menu) | |
2311 | (append cor-menu (list "-" save session buffer)) | |
2312 | (list save session buffer))))) | |
2313 | (popup-menu (cons (format "%s [%s]" word (or ispell-local-dictionary | |
2314 | ispell-dictionary)) | |
2315 | menu)))) | |
2316 | ||
042c6fb7 SM |
2317 | ;;*---------------------------------------------------------------------*/ |
2318 | ;;* Some example functions for real autocorrecting */ | |
2319 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 | 2320 | (defun flyspell-maybe-correct-transposition (beg end poss) |
25f2ad05 GM |
2321 | "Check replacements for transposed characters. |
2322 | ||
2323 | If the text between BEG and END is equal to a correction suggested by | |
2324 | Ispell, after transposing two adjacent characters, correct the text, | |
2325 | and return t. | |
2326 | ||
2327 | The third arg POSS is either the symbol 'doublon' or a list of | |
ebb7de84 | 2328 | possible corrections as returned by `ispell-parse-output'. |
3215afc4 | 2329 | |
ebb7de84 | 2330 | This function is meant to be added to `flyspell-incorrect-hook'." |
25f2ad05 | 2331 | (when (consp poss) |
4c685fb8 JW |
2332 | (catch 'done |
2333 | (let ((str (buffer-substring beg end)) | |
2334 | (i 0) (len (- end beg)) tmp) | |
2335 | (while (< (1+ i) len) | |
2336 | (setq tmp (aref str i)) | |
2337 | (aset str i (aref str (1+ i))) | |
2338 | (aset str (1+ i) tmp) | |
2339 | (when (member str (nth 2 poss)) | |
2340 | (save-excursion | |
2341 | (goto-char (+ beg i 1)) | |
2342 | (transpose-chars 1)) | |
2343 | (throw 'done t)) | |
2344 | (setq tmp (aref str i)) | |
2345 | (aset str i (aref str (1+ i))) | |
2346 | (aset str (1+ i) tmp) | |
2347 | (setq i (1+ i)))) | |
2348 | nil))) | |
3215afc4 GM |
2349 | |
2350 | (defun flyspell-maybe-correct-doubling (beg end poss) | |
25f2ad05 GM |
2351 | "Check replacements for doubled characters. |
2352 | ||
2353 | If the text between BEG and END is equal to a correction suggested by | |
2354 | Ispell, after removing a pair of doubled characters, correct the text, | |
2355 | and return t. | |
2356 | ||
2357 | The third arg POSS is either the symbol 'doublon' or a list of | |
ebb7de84 | 2358 | possible corrections as returned by `ispell-parse-output'. |
3215afc4 | 2359 | |
ebb7de84 | 2360 | This function is meant to be added to `flyspell-incorrect-hook'." |
1f44857f | 2361 | (when (consp poss) |
4c685fb8 JW |
2362 | (catch 'done |
2363 | (let ((str (buffer-substring beg end)) | |
2364 | (i 0) (len (- end beg))) | |
2365 | (while (< (1+ i) len) | |
2366 | (when (and (= (aref str i) (aref str (1+ i))) | |
2367 | (member (concat (substring str 0 (1+ i)) | |
2368 | (substring str (+ i 2))) | |
2369 | (nth 2 poss))) | |
2370 | (goto-char (+ beg i)) | |
2371 | (delete-char 1) | |
2372 | (throw 'done t)) | |
2373 | (setq i (1+ i)))) | |
2374 | nil))) | |
3215afc4 | 2375 | |
042c6fb7 SM |
2376 | ;;*---------------------------------------------------------------------*/ |
2377 | ;;* flyspell-already-abbrevp ... */ | |
2378 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
2379 | (defun flyspell-already-abbrevp (table word) |
2380 | (let ((sym (abbrev-symbol word table))) | |
2381 | (and sym (symbolp sym)))) | |
60371a2e | 2382 | |
042c6fb7 SM |
2383 | ;;*---------------------------------------------------------------------*/ |
2384 | ;;* flyspell-change-abbrev ... */ | |
2385 | ;;*---------------------------------------------------------------------*/ | |
3215afc4 GM |
2386 | (defun flyspell-change-abbrev (table old new) |
2387 | (set (abbrev-symbol old table) new)) | |
2fced6f9 | 2388 | |
3215afc4 | 2389 | (provide 'flyspell) |
e8af40ee | 2390 | |
60371a2e | 2391 | ;;; flyspell.el ends here |