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