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