root/trunk/lisp/textmodes/flyspell.el

Revision 4220, 92.7 kB (checked in by miyoshi, 9 months ago)

Sync up with Emacs22.2.

  • Property svn:eol-style set to LF
Line 
1 ;;; flyspell.el --- on-the-fly spell checker
2
3 ;; Copyright (C) 1998, 2000, 2001, 2002, 2003, 2004,
4 ;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
6 ;; Author: Manuel Serrano <Manuel.Serrano@sophia.inria.fr>
7 ;; Maintainer: FSF
8 ;; Keywords: convenience
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 3, or (at your option)
15 ;; any later version.
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
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
26
27 ;;; Commentary:
28 ;;
29 ;; Flyspell is a minor Emacs mode performing on-the-fly spelling
30 ;; checking.
31 ;;
32 ;; To enable Flyspell minor mode, type M-x flyspell-mode.
33 ;; This applies only to the current buffer.
34 ;;
35 ;; To enable Flyspell in text representing computer programs, type
36 ;; M-x flyspell-prog-mode.
37 ;; In that mode only text inside comments is checked.
38 ;;
39 ;; Note: consider setting the variable ispell-parser to `tex' to
40 ;; avoid TeX command checking; use `(setq ispell-parser 'tex)'.
41 ;;
42 ;; Some user variables control the behavior of flyspell.  They are
43 ;; those defined under the `User variables' comment.
44
45 ;;; Code:
46
47 (require 'ispell)
48
49 ;;*---------------------------------------------------------------------*/
50 ;;*    Group ...                                                        */
51 ;;*---------------------------------------------------------------------*/
52 (defgroup flyspell nil
53   "Spell checking on the fly."
54   :tag "FlySpell"
55   :prefix "flyspell-"
56   :group 'ispell
57   :group 'processes)
58
59 ;;*---------------------------------------------------------------------*/
60 ;;*    User configuration ...                                           */
61 ;;*---------------------------------------------------------------------*/
62 (defcustom flyspell-highlight-flag t
63   "How Flyspell should indicate misspelled words.
64 Non-nil means use highlight, nil means use minibuffer messages."
65   :group 'flyspell
66   :type 'boolean)
67
68 (defcustom flyspell-mark-duplications-flag t
69   "Non-nil means Flyspell reports a repeated word as an error.
70 Detection of repeated words is not implemented in
71 \"large\" regions; see `flyspell-large-region'."
72   :group 'flyspell
73   :type 'boolean)
74
75 (defcustom flyspell-sort-corrections nil
76   "Non-nil means, sort the corrections alphabetically before popping them."
77   :group 'flyspell
78   :version "21.1"
79   :type 'boolean)
80
81 (defcustom flyspell-duplicate-distance -1
82   "The maximum distance for finding duplicates of unrecognized words.
83 This applies to the feature that when a word is not found in the dictionary,
84 if the same spelling occurs elsewhere in the buffer,
85 Flyspell uses a different face (`flyspell-duplicate') to highlight it.
86 This variable specifies how far to search to find such a duplicate.
87 -1 means no limit (search the whole buffer).
88 0 means do not search for duplicate unrecognized spellings."
89   :group 'flyspell
90   :version "21.1"
91   :type 'number)
92
93 (defcustom flyspell-delay 3
94   "The number of seconds to wait before checking, after a \"delayed\" command."
95   :group 'flyspell
96   :type 'number)
97
98 (defcustom flyspell-persistent-highlight t
99   "Non-nil means misspelled words remain highlighted until corrected.
100 If this variable is nil, only the most recently detected misspelled word
101 is highlighted."
102   :group 'flyspell
103   :type 'boolean)
104
105 (defcustom flyspell-highlight-properties t
106   "Non-nil means highlight incorrect words even if a property exists for this word."
107   :group 'flyspell
108   :type 'boolean)
109
110 (defcustom flyspell-default-delayed-commands
111   '(self-insert-command
112     delete-backward-char
113     backward-or-forward-delete-char
114     delete-char
115     scrollbar-vertical-drag
116     backward-delete-char-untabify)
117   "The standard list of delayed commands for Flyspell.
118 See `flyspell-delayed-commands'."
119   :group 'flyspell
120   :version "21.1"
121   :type '(repeat (symbol)))
122
123 (defcustom flyspell-delayed-commands nil
124   "List of commands that are \"delayed\" for Flyspell mode.
125 After these commands, Flyspell checking is delayed for a short time,
126 whose length is specified by `flyspell-delay'."
127   :group 'flyspell
128   :type '(repeat (symbol)))
129
130 (defcustom flyspell-default-deplacement-commands
131   '(next-line
132     previous-line
133     scroll-up
134     scroll-down)
135   "The standard list of deplacement commands for Flyspell.
136 See `flyspell-deplacement-commands'."
137   :group 'flyspell
138   :version "21.1"
139   :type '(repeat (symbol)))
140
141 (defcustom flyspell-deplacement-commands nil
142   "List of commands that are \"deplacement\" for Flyspell mode.
143 After these commands, Flyspell checking is performed only if the previous
144 command was not the very same command."
145   :group 'flyspell
146   :version "21.1"
147   :type '(repeat (symbol)))
148
149 (defcustom flyspell-issue-welcome-flag t
150   "Non-nil means that Flyspell should display a welcome message when started."
151   :group 'flyspell
152   :type 'boolean)
153
154 (defcustom flyspell-issue-message-flag t
155   "Non-nil means that Flyspell emits messages when checking words."
156   :group 'flyspell
157   :type 'boolean)
158
159 (defcustom flyspell-incorrect-hook nil
160   "List of functions to be called when incorrect words are encountered.
161 Each function is given three arguments.  The first two
162 arguments are the beginning and the end of the incorrect region.
163 The third is either the symbol `doublon' or the list
164 of possible corrections as returned by `ispell-parse-output'.
165
166 If any of the functions return non-nil, the word is not highlighted as
167 incorrect."
168   :group 'flyspell
169   :version "21.1"
170   :type 'hook)
171
172 (defcustom flyspell-default-dictionary nil
173   "A string that is the name of the default dictionary.
174 This is passed to the `ispell-change-dictionary' when flyspell is started.
175 If the variable `ispell-local-dictionary' or `ispell-dictionary' is non-nil
176 when flyspell is started, the value of that variable is used instead
177 of `flyspell-default-dictionary' to select the default dictionary.
178 Otherwise, if `flyspell-default-dictionary' is nil, it means to use
179 Ispell's ultimate default dictionary."
180   :group 'flyspell
181   :version "21.1"
182   :type '(choice string (const :tag "Default" nil)))
183
184 (defcustom flyspell-tex-command-regexp
185   "\\(\\(begin\\|end\\)[ \t]*{\\|\\(cite[a-z*]*\\|label\\|ref\\|eqref\\|usepackage\\|documentclass\\)[ \t]*\\(\\[[^]]*\\]\\)?{[^{}]*\\)"
186   "A string that is the regular expression that matches TeX commands."
187   :group 'flyspell
188   :version "21.1"
189   :type 'string)
190
191 (defcustom flyspell-check-tex-math-command nil
192   "Non-nil means check even inside TeX math environment.
193 TeX math environments are discovered by the TEXMATHP that implemented
194 inside the texmathp.el Emacs package.  That package may be found at:
195 http://strw.leidenuniv.nl/~dominik/Tools"
196   :group 'flyspell
197   :type 'boolean)
198
199 (defcustom flyspell-dictionaries-that-consider-dash-as-word-delimiter
200   '("francais" "deutsch8" "norsk")
201   "List of dictionary names that consider `-' as word delimiter."
202   :group 'flyspell
203   :version "21.1"
204   :type '(repeat (string)))
205
206 (defcustom flyspell-abbrev-p
207   nil
208   "If non-nil, add correction to abbreviation table."
209   :group 'flyspell
210   :version "21.1"
211   :type 'boolean)
212
213 (defcustom flyspell-use-global-abbrev-table-p
214   nil
215   "If non-nil, prefer global abbrev table to local abbrev table."
216   :group 'flyspell
217   :version "21.1"
218   :type 'boolean)
219
220 (defcustom flyspell-mode-line-string " Fly"
221   "String displayed on the modeline when flyspell is active.
222 Set this to nil if you don't want a modeline indicator."
223   :group 'flyspell
224   :type '(choice string (const :tag "None" nil)))
225
226 (defcustom flyspell-large-region 1000
227   "The threshold that determines if a region is small.
228 If the region is smaller than this number of characters,
229 `flyspell-region' checks the words sequentially using regular
230 flyspell methods.  Else, if the region is large, a new Ispell process is
231 spawned for speed.
232
233 Doubled words are not detected in a large region, because Ispell
234 does not check for them.
235
236 If `flyspell-large-region' is nil, all regions are treated as small."
237   :group 'flyspell
238   :version "21.1"
239   :type '(choice number (const :tag "All small" nil)))
240
241 (defcustom flyspell-insert-function (function insert)
242   "Function for inserting word by flyspell upon correction."
243   :group 'flyspell
244   :type 'function)
245
246 (defcustom flyspell-before-incorrect-word-string nil
247   "String used to indicate an incorrect word starting."
248   :group 'flyspell
249   :type '(choice string (const nil)))
250
251 (defcustom flyspell-after-incorrect-word-string nil
252   "String used to indicate an incorrect word ending."
253   :group 'flyspell
254   :type '(choice string (const nil)))
255
256 (defcustom flyspell-use-meta-tab t
257   "Non-nil means that flyspell uses M-TAB to correct word."
258   :group 'flyspell
259   :type 'boolean)
260
261 (defcustom flyspell-auto-correct-binding
262   [(control ?\;)]
263   "The key binding for flyspell auto correction."
264   :group 'flyspell)
265
266 ;;*---------------------------------------------------------------------*/
267 ;;*    Mode specific options                                            */
268 ;;*    -------------------------------------------------------------    */
269 ;;*    Mode specific options enable users to disable flyspell on        */
270 ;;*    certain word depending of the emacs mode. For instance, when     */
271 ;;*    using flyspell with mail-mode add the following expression       */
272 ;;*    in your .emacs file:                                             */
273 ;;*       (add-hook 'mail-mode                                          */
274 ;;*          '(lambda () (setq flyspell-generic-check-word-predicate    */
275 ;;*                            'mail-mode-flyspell-verify)))            */
276 ;;*---------------------------------------------------------------------*/
277 (defvar flyspell-generic-check-word-predicate nil
278   "Function providing per-mode customization over which words are flyspelled.
279 Returns t to continue checking, nil otherwise.
280 Flyspell mode sets this variable to whatever is the `flyspell-mode-predicate'
281 property of the major mode name.")
282 (make-variable-buffer-local 'flyspell-generic-check-word-predicate)
283 (defvaralias 'flyspell-generic-check-word-p
284   'flyspell-generic-check-word-predicate)
285
286 ;;*--- mail mode -------------------------------------------------------*/
287 (put 'mail-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify)
288 (put 'message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify)
289 (defun mail-mode-flyspell-verify ()
290   "Function used for `flyspell-generic-check-word-predicate' in Mail mode."
291   (let ((header-end (save-excursion
292                       (goto-char (point-min))
293                       (re-search-forward
294                        (concat "^"
295                                (regexp-quote mail-header-separator)
296                                "$")
297                        nil t)
298                       (point)))
299         (signature-begin (save-excursion
300                            (goto-char (point-max))
301                            (re-search-backward message-signature-separator
302                                                nil t)
303                            (point))))
304     (cond ((< (point) header-end)
305            (and (save-excursion (beginning-of-line)
306                                 (looking-at "^Subject:"))
307                 (> (point) (match-end 0))))
308           ((> (point) signature-begin)
309            nil)
310           (t
311            (save-excursion
312              (beginning-of-line)
313              (not (looking-at "[>}|]\\|To:")))))))
314
315 ;;*--- texinfo mode ----------------------------------------------------*/
316 (put 'texinfo-mode 'flyspell-mode-predicate 'texinfo-mode-flyspell-verify)
317 (defun texinfo-mode-flyspell-verify ()
318   "Function used for `flyspell-generic-check-word-predicate' in Texinfo mode."
319   (save-excursion
320     (forward-word -1)
321     (not (looking-at "@"))))
322
323 ;;*--- tex mode --------------------------------------------------------*/
324 (put 'tex-mode 'flyspell-mode-predicate 'tex-mode-flyspell-verify)
325 (defun tex-mode-flyspell-verify ()
326   "Function used for `flyspell-generic-check-word-predicate' in LaTeX mode."
327   (and
328    (not (save-excursion
329           (re-search-backward "^[ \t]*%%%[ \t]+Local" nil t)))
330    (not (save-excursion
331           (let ((this (point-marker))
332                 (e (progn (end-of-line) (point-marker))))
333             (beginning-of-line)
334             (if (re-search-forward "\\\\\\(cite\\|label\\|ref\\){[^}]*}" e t)
335                 (and (>= this (match-beginning 0))
336                      (<= this (match-end 0)) )))))))
337
338 ;;*--- sgml mode -------------------------------------------------------*/
339 (put 'sgml-mode 'flyspell-mode-predicate 'sgml-mode-flyspell-verify)
340 (put 'html-mode 'flyspell-mode-predicate 'sgml-mode-flyspell-verify)
341
342 (defun sgml-mode-flyspell-verify ()
343   "Function used for `flyspell-generic-check-word-predicate' in SGML mode."
344   (not (save-excursion
345          (let ((this (point-marker))
346                (s (progn (beginning-of-line) (point-marker)))
347                (e (progn (end-of-line) (point-marker))))
348            (or (progn
349                  (goto-char this)
350                  (and (re-search-forward  "[^<]*>" e t)
351                       (= (match-beginning 0) this)))
352                (progn
353                  (goto-char this)
354                  (and (re-search-backward "<[^>]*" s t)
355                       (= (match-end 0) this)))
356                (and (progn
357                       (goto-char this)
358                       (and (re-search-forward  "[^&]*;" e t)
359                            (= (match-beginning 0) this)))
360                     (progn
361                       (goto-char this)
362                       (and (re-search-backward "&[^;]*" s t)
363                            (= (match-end 0) this)))))))))
364
365 ;;*---------------------------------------------------------------------*/
366 ;;*    Programming mode                                                 */
367 ;;*---------------------------------------------------------------------*/
368 (defvar flyspell-prog-text-faces
369   '(font-lock-string-face font-lock-comment-face font-lock-doc-face)
370   "Faces corresponding to text in programming-mode buffers.")
371
372 (defun flyspell-generic-progmode-verify ()
373   "Used for `flyspell-generic-check-word-predicate' in programming modes."
374   (let ((f (get-text-property (point) 'face)))
375     (memq f flyspell-prog-text-faces)))
376
377 ;;;###autoload
378 (defun flyspell-prog-mode ()
379   "Turn on `flyspell-mode' for comments and strings."
380   (interactive)
381   (setq flyspell-generic-check-word-predicate
382         'flyspell-generic-progmode-verify)
383   (flyspell-mode 1)
384   (run-hooks 'flyspell-prog-mode-hook))
385
386 ;;*---------------------------------------------------------------------*/
387 ;;*    Overlay compatibility                                            */
388 ;;*---------------------------------------------------------------------*/
389 (autoload 'make-overlay "overlay" "Overlay compatibility kit." t)
390 (autoload 'overlayp                "overlay" "Overlay compatibility kit." t)
391 (autoload 'overlays-in             "overlay" "Overlay compatibility kit." t)
392 (autoload 'delete-overlay          "overlay" "Overlay compatibility kit." t)
393 (autoload 'overlays-at             "overlay" "Overlay compatibility kit." t)
394 (autoload 'overlay-put             "overlay" "Overlay compatibility kit." t)
395 (autoload 'overlay-get             "overlay" "Overlay compatibility kit." t)
396 (autoload 'previous-overlay-change "overlay" "Overlay compatibility kit." t)
397
398 ;;*---------------------------------------------------------------------*/
399 ;;*    The minor mode declaration.                                      */
400 ;;*---------------------------------------------------------------------*/
401 (defvar flyspell-mouse-map
402   (let ((map (make-sparse-keymap)))
403     (define-key map (if (featurep 'xemacs) [button2] [down-mouse-2])
404       #'flyspell-correct-word)
405     map)
406   "Keymap for Flyspell to put on erroneous words.")
407
408 (defvar flyspell-mode-map
409   (let ((map (make-sparse-keymap)))
410     (if flyspell-use-meta-tab
411       (define-key map "\M-\t" 'flyspell-auto-correct-word))
412     (define-key map flyspell-auto-correct-binding 'flyspell-auto-correct-previous-word)
413     (define-key map [(control ?\,)] 'flyspell-goto-next-error)
414     (define-key map [(control ?\.)] 'flyspell-auto-correct-word)
415     (define-key map [?\C-c ?$] 'flyspell-correct-word-before-point)
416     map)
417   "Minor mode keymap for Flyspell mode--for the whole buffer.")
418
419 ;; dash character machinery
420 (defvar flyspell-consider-dash-as-word-delimiter-flag nil
421    "*Non-nil means that the `-' char is considered as a word delimiter.")
422 (make-variable-buffer-local 'flyspell-consider-dash-as-word-delimiter-flag)
423 (defvar flyspell-dash-dictionary nil)
424 (make-variable-buffer-local 'flyspell-dash-dictionary)
425 (defvar flyspell-dash-local-dictionary nil)
426 (make-variable-buffer-local 'flyspell-dash-local-dictionary)
427
428 ;;*---------------------------------------------------------------------*/
429 ;;*    Highlighting                                                     */
430 ;;*---------------------------------------------------------------------*/
431 (defface flyspell-incorrect
432   '((((class color)) (:foreground "OrangeRed" :bold t :underline t))
433     (t (:bold t)))
434   "Face used for marking a misspelled word in Flyspell."
435   :group 'flyspell)
436 ;; backward-compatibility alias
437 (put 'flyspell-incorrect-face 'face-alias 'flyspell-incorrect)
438
439 (defface flyspell-duplicate
440   '((((class color)) (:foreground "Gold3" :bold t :underline t))
441     (t (:bold t)))
442   "Face used for marking a misspelled word that appears twice in the buffer.
443 See also `flyspell-duplicate-distance'."
444   :group 'flyspell)
445 ;; backward-compatibility alias
446 (put 'flyspell-duplicate-face 'face-alias 'flyspell-duplicate)
447
448 (defvar flyspell-overlay nil)
449
450 ;;*---------------------------------------------------------------------*/
451 ;;*    flyspell-mode ...                                                */
452 ;;*---------------------------------------------------------------------*/
453 ;;;###autoload(defvar flyspell-mode nil)
454 ;;;###autoload
455 (define-minor-mode flyspell-mode
456   "Minor mode performing on-the-fly spelling checking.
457 This spawns a single Ispell process and checks each word.
458 The default flyspell behavior is to highlight incorrect words.
459 With no argument, this command toggles Flyspell mode.
460 With a prefix argument ARG, turn Flyspell minor mode on if ARG is positive,
461 otherwise turn it off.
462
463 Bindings:
464 \\[ispell-word]: correct words (using Ispell).
465 \\[flyspell-auto-correct-word]: automatically correct word.
466 \\[flyspell-auto-correct-previous-word]: automatically correct the last misspelled word.
467 \\[flyspell-correct-word] (or down-mouse-2): popup correct words.
468
469 Hooks:
470 This runs `flyspell-mode-hook' after flyspell is entered.
471
472 Remark:
473 `flyspell-mode' uses `ispell-mode'.  Thus all Ispell options are
474 valid.  For instance, a personal dictionary can be used by
475 invoking `ispell-change-dictionary'.
476
477 Consider using the `ispell-parser' to check your text.  For instance
478 consider adding:
479 \(add-hook 'tex-mode-hook (function (lambda () (setq ispell-parser 'tex))))
480 in your .emacs file.
481
482 \\[flyspell-region] checks all words inside a region.
483 \\[flyspell-buffer] checks the whole buffer."
484   :lighter flyspell-mode-line-string
485   :keymap flyspell-mode-map
486   :group 'flyspell
487   (if flyspell-mode
488       (condition-case ()
489           (flyspell-mode-on)
490         (error (message "Enabling Flyspell mode gave an error")
491                (flyspell-mode -1)))
492     (flyspell-mode-off)))
493
494 ;;;###autoload
495 (defun turn-on-flyspell ()
496   "Unconditionally turn on Flyspell mode."
497   (flyspell-mode 1))
498
499 ;;;###autoload
500 (defun turn-off-flyspell ()
501   "Unconditionally turn off Flyspell mode."
502   (flyspell-mode -1))
503
504 (custom-add-option 'text-mode-hook 'turn-on-flyspell)
505
506 ;;*---------------------------------------------------------------------*/
507 ;;*    flyspell-buffers ...                                             */
508 ;;*    -------------------------------------------------------------    */
509 ;;*    For remembering buffers running flyspell                         */
510 ;;*---------------------------------------------------------------------*/
511 (defvar flyspell-buffers nil)
512
513 ;;*---------------------------------------------------------------------*/
514 ;;*    flyspell-minibuffer-p ...                                        */
515 ;;*---------------------------------------------------------------------*/
516 (defun flyspell-minibuffer-p (buffer)
517   "Is BUFFER a minibuffer?"
518   (let ((ws (get-buffer-window-list buffer t)))
519     (and (consp ws) (window-minibuffer-p (car ws)))))
520
521 ;;*---------------------------------------------------------------------*/
522 ;;*    flyspell-accept-buffer-local-defs ...                            */
523 ;;*---------------------------------------------------------------------*/
524 (defvar flyspell-last-buffer nil
525   "The buffer in which the last flyspell operation took place.")
526
527 (defun flyspell-accept-buffer-local-defs (&optional force)
528   ;; When flyspell-word is used inside a loop (e.g. when processing
529   ;; flyspell-changes), the calls to `ispell-accept-buffer-local-defs' end
530   ;; up dwarfing everything else, so only do it when the buffer has changed.
531   (when (or force (not (eq flyspell-last-buffer (current-buffer))))
532     (setq flyspell-last-buffer (current-buffer))
533     ;; Strange problem:  If buffer in current window has font-lock turned on,
534     ;; but SET-BUFFER was called to point to an invisible buffer, this ispell
535     ;; call will reset the buffer to the buffer in the current window.
536     ;; However, it only happens at startup (fix by Albert L. Ting).
537     (save-current-buffer
538       (ispell-accept-buffer-local-defs))
539     (unless (and (eq flyspell-dash-dictionary ispell-dictionary)
540                  (eq flyspell-dash-local-dictionary ispell-local-dictionary))
541       ;; The dictionary has changed
542       (setq flyspell-dash-dictionary ispell-dictionary)
543       (setq flyspell-dash-local-dictionary ispell-local-dictionary)
544       (setq flyspell-consider-dash-as-word-delimiter-flag
545             (member (or ispell-local-dictionary ispell-dictionary)
546                     flyspell-dictionaries-that-consider-dash-as-word-delimiter)))))
547
548 (defun flyspell-hack-local-variables-hook ()
549   ;; When local variables are loaded, see if the dictionary context
550   ;; has changed.
551   (flyspell-accept-buffer-local-defs 'force))
552
553 (defun flyspell-kill-ispell-hook ()
554   (setq flyspell-last-buffer nil)
555   (dolist (buf (buffer-list))
556     (with-current-buffer buf
557       (kill-local-variable 'flyspell-word-cache-word))))
558
559 ;; Make sure we flush our caches when needed.  Do it here rather than in
560 ;; flyspell-mode-on, since flyspell-region may be used without ever turning
561 ;; on flyspell-mode.
562 (add-hook 'ispell-kill-ispell-hook 'flyspell-kill-ispell-hook)
563
564 ;;*---------------------------------------------------------------------*/
565 ;;*    flyspell-mode-on ...                                             */
566 ;;*---------------------------------------------------------------------*/
567 (defun flyspell-mode-on ()
568   "Turn Flyspell mode on.  Do not use this; use `flyspell-mode' instead."
569   (ispell-maybe-find-aspell-dictionaries)
570   (setq ispell-highlight-face 'flyspell-incorrect)
571   ;; local dictionaries setup
572   (or ispell-local-dictionary ispell-dictionary
573       (if flyspell-default-dictionary
574           (ispell-change-dictionary flyspell-default-dictionary)))
575   ;; we have to force ispell to accept the local definition or
576   ;; otherwise it could be too late, the local dictionary may
577   ;; be forgotten!
578   ;; Pass the `force' argument for the case where flyspell was active already
579   ;; but the buffer's local-defs have been edited.
580   (flyspell-accept-buffer-local-defs 'force)
581   ;; we put the `flyspell-delayed' property on some commands
582   (flyspell-delay-commands)
583   ;; we put the `flyspell-deplacement' property on some commands
584   (flyspell-deplacement-commands)
585   ;; we bound flyspell action to post-command hook
586   (add-hook 'post-command-hook (function flyspell-post-command-hook) t t)
587   ;; we bound flyspell action to pre-command hook
588   (add-hook 'pre-command-hook (function flyspell-pre-command-hook) t t)
589   ;; we bound flyspell action to after-change hook
590   (add-hook 'after-change-functions 'flyspell-after-change-function nil t)
591   ;; we bound flyspell action to hack-local-variables-hook
592   (add-hook 'hack-local-variables-hook
593             (function flyspell-hack-local-variables-hook) t t)
594   ;; set flyspell-generic-check-word-predicate based on the major mode
595   (let ((mode-predicate (get major-mode 'flyspell-mode-predicate)))
596     (if mode-predicate
597         (setq flyspell-generic-check-word-predicate mode-predicate)))
598   ;; the welcome message
599   (if (and flyspell-issue-message-flag
600            flyspell-issue-welcome-flag
601            (interactive-p))
602       (let ((binding (where-is-internal 'flyspell-auto-correct-word
603                                         nil 'non-ascii)))
604         (message "%s"
605          (if binding
606              (format "Welcome to flyspell. Use %s or Mouse-2 to correct words."
607                      (key-description binding))
608            "Welcome to flyspell. Use Mouse-2 to correct words."))))
609   ;; we end with the flyspell hooks
610   (run-hooks 'flyspell-mode-hook))
611
612 ;;*---------------------------------------------------------------------*/
613 ;;*    flyspell-delay-commands ...                                      */
614 ;;*---------------------------------------------------------------------*/
615 (defun flyspell-delay-commands ()
616   "Install the standard set of Flyspell delayed commands."
617   (mapcar 'flyspell-delay-command flyspell-default-delayed-commands)
618   (mapcar 'flyspell-delay-command flyspell-delayed-commands))
619
620 ;;*---------------------------------------------------------------------*/
621 ;;*    flyspell-delay-command ...                                       */
622 ;;*---------------------------------------------------------------------*/
623 (defun flyspell-delay-command (command)
624   "Set COMMAND to be delayed, for Flyspell.
625 When flyspell `post-command-hook' is invoked because a delayed command
626 as been used the current word is not immediately checked.
627 It will be checked only after `flyspell-delay' seconds."
628   (interactive "SDelay Flyspell after Command: ")
629   (put command 'flyspell-delayed t))
630
631 ;;*---------------------------------------------------------------------*/
632 ;;*    flyspell-deplacement-commands ...                                */
633 ;;*---------------------------------------------------------------------*/
634 (defun flyspell-deplacement-commands ()
635   "Install the standard set of Flyspell deplacement commands."
636   (mapcar 'flyspell-deplacement-command flyspell-default-deplacement-commands)
637   (mapcar 'flyspell-deplacement-command flyspell-deplacement-commands))
638
639 ;;*---------------------------------------------------------------------*/
640 ;;*    flyspell-deplacement-command ...                                 */
641 ;;*---------------------------------------------------------------------*/
642 (defun flyspell-deplacement-command (command)
643   "Set COMMAND that implement cursor movements, for Flyspell.
644 When flyspell `post-command-hook' is invoked because of a deplacement command
645 as been used the current word is checked only if the previous command was
646 not the very same deplacement command."
647   (interactive "SDeplacement Flyspell after Command: ")
648   (put command 'flyspell-deplacement t))
649
650 ;;*---------------------------------------------------------------------*/
651 ;;*    flyspell-word-cache ...                                          */
652 ;;*---------------------------------------------------------------------*/
653 (defvar flyspell-word-cache-start  nil)
654 (defvar flyspell-word-cache-end    nil)
655 (defvar flyspell-word-cache-word   nil)
656 (defvar flyspell-word-cache-result '_)
657 (make-variable-buffer-local 'flyspell-word-cache-start)
658 (make-variable-buffer-local 'flyspell-word-cache-end)
659 (make-variable-buffer-local 'flyspell-word-cache-word)
660 (make-variable-buffer-local 'flyspell-word-cache-result)
661
662 ;;*---------------------------------------------------------------------*/
663 ;;*    The flyspell pre-hook, store the current position. In the        */
664 ;;*    post command hook, we will check, if the word at this position   */
665 ;;*    has to be spell checked.                                         */
666 ;;*---------------------------------------------------------------------*/
667 (defvar flyspell-pre-buffer     nil)
668 (defvar flyspell-pre-point      nil)
669 (defvar flyspell-pre-column     nil)
670 (defvar flyspell-pre-pre-buffer nil)
671 (defvar flyspell-pre-pre-point  nil)
672
673 ;;*---------------------------------------------------------------------*/
674 ;;*    flyspell-previous-command ...                                    */
675 ;;*---------------------------------------------------------------------*/
676 (defvar flyspell-previous-command nil
677   "The last interactive command checked by Flyspell.")
678
679 ;;*---------------------------------------------------------------------*/
680 ;;*    flyspell-pre-command-hook ...                                    */
681 ;;*---------------------------------------------------------------------*/
682 (defun flyspell-pre-command-hook ()
683   "Save the current buffer and point for Flyspell's post-command hook."
684   (interactive)
685   (setq flyspell-pre-buffer (current-buffer))
686   (setq flyspell-pre-point  (point))
687   (setq flyspell-pre-column (current-column)))
688
689 ;;*---------------------------------------------------------------------*/
690 ;;*    flyspell-mode-off ...                                            */
691 ;;*---------------------------------------------------------------------*/
692 ;;;###autoload
693 (defun flyspell-mode-off ()
694   "Turn Flyspell mode off."
695   ;; we remove the hooks
696   (remove-hook 'post-command-hook (function flyspell-post-command-hook) t)
697   (remove-hook 'pre-command-hook (function flyspell-pre-command-hook) t)
698   (remove-hook 'after-change-functions 'flyspell-after-change-function t)
699   (remove-hook 'hack-local-variables-hook
700                (function flyspell-hack-local-variables-hook) t)
701   ;; we remove all the flyspell hilightings
702   (flyspell-delete-all-overlays)
703   ;; we have to erase pre cache variables
704   (setq flyspell-pre-buffer nil)
705   (setq flyspell-pre-point  nil)
706   ;; we mark the mode as killed
707   (setq flyspell-mode nil))
708
709 ;;*---------------------------------------------------------------------*/
710 ;;*    flyspell-check-pre-word-p ...                                    */
711 ;;*---------------------------------------------------------------------*/
712 (defun flyspell-check-pre-word-p ()
713   "Return non-nil if we should check the word before point.
714 More precisely, it applies to the word that was before point
715 before the current command."
716   (cond
717    ((or (not (numberp flyspell-pre-point))
718         (not (bufferp flyspell-pre-buffer))
719         (not (buffer-live-p flyspell-pre-buffer)))
720     nil)
721    ((and (eq flyspell-pre-pre-point flyspell-pre-point)
722          (eq flyspell-pre-pre-buffer flyspell-pre-buffer))
723     nil)
724    ((or (and (= flyspell-pre-point (- (point) 1))
725              (eq (char-syntax (char-after flyspell-pre-point)) ?w))
726         (= flyspell-pre-point (point))
727         (= flyspell-pre-point (+ (point) 1)))
728     nil)
729    ((and (symbolp this-command)
730          (not executing-kbd-macro)
731          (or (get this-command 'flyspell-delayed)
732              (and (get this-command 'flyspell-deplacement)
733                   (eq flyspell-previous-command this-command)))
734          (or (= (current-column) 0)
735              (= (current-column) flyspell-pre-column)
736              (eq (char-syntax (char-after flyspell-pre-point)) ?w)))
737     nil)
738    ((not (eq (current-buffer) flyspell-pre-buffer))
739     t)
740    ((not (and (numberp flyspell-word-cache-start)
741               (numberp flyspell-word-cache-end)))
742     t)
743    (t
744     (or (< flyspell-pre-point flyspell-word-cache-start)
745         (> flyspell-pre-point flyspell-word-cache-end)))))
746
747 ;;*---------------------------------------------------------------------*/
748 ;;*    The flyspell after-change-hook, store the change position. In    */
749 ;;*    the post command hook, we will check, if the word at this        */
750 ;;*    position has to be spell checked.                                */
751 ;;*---------------------------------------------------------------------*/
752 (defvar flyspell-changes nil)
753 (make-variable-buffer-local 'flyspell-changes)
754
755 ;;*---------------------------------------------------------------------*/
756 ;;*    flyspell-after-change-function ...                               */
757 ;;*---------------------------------------------------------------------*/
758 (defun flyspell-after-change-function (start stop len)
759   "Save the current buffer and point for Flyspell's post-command hook."
760   (push (cons start stop) flyspell-changes))
761
762 ;;*---------------------------------------------------------------------*/
763 ;;*    flyspell-check-changed-word-p ...                                */
764 ;;*---------------------------------------------------------------------*/
765 (defun flyspell-check-changed-word-p (start stop)
766   "Return t when the changed word has to be checked.
767 The answer depends of several criteria.
768 Mostly we check word delimiters."
769   (cond
770    ((and (memq (char-after start) '(?\n ? )) (> stop start))
771     t)
772    ((not (numberp flyspell-pre-point))
773     t)
774    ((and (>= flyspell-pre-point start) (<= flyspell-pre-point stop))
775     nil)
776    ((let ((pos (point)))
777       (or (>= pos start) (<= pos stop) (= pos (1+ stop))))
778     nil)
779    (t
780     t)))
781
782 ;;*---------------------------------------------------------------------*/
783 ;;*    flyspell-check-word-p ...                                        */
784 ;;*---------------------------------------------------------------------*/
785 (defun flyspell-check-word-p ()
786   "Return t when the word at `point' has to be checked.
787 The answer depends of several criteria.
788 Mostly we check word delimiters."
789   (cond
790    ((<= (- (point-max) 1) (point-min))
791     ;; the buffer is not filled enough
792     nil)
793    ((and (and (> (current-column) 0)
794               (not (eq (current-column) flyspell-pre-column)))
795          (save-excursion
796            (backward-char 1)
797            (and (looking-at (flyspell-get-not-casechars))
798                 (or flyspell-consider-dash-as-word-delimiter-flag
799                     (not (looking-at "-"))))))