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