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