flyspell.el 84.8 KB
Newer Older
1
;;; flyspell.el --- on-the-fly spell checker
Richard M. Stallman's avatar
Richard M. Stallman committed
2

3
;; Copyright (C) 1998, 2000, 2003, 2004, 2005 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
4

5
;; Author: Manuel Serrano <Manuel.Serrano@sophia.inria.fr>
Richard M. Stallman's avatar
Richard M. Stallman committed
6
;; Maintainer: FSF
7
;; Keywords: convenience
Richard M. Stallman's avatar
Richard M. Stallman committed
8

9
;; This file is part of GNU Emacs.
Richard M. Stallman's avatar
Richard M. Stallman committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

26
;;; Commentary:
Richard M. Stallman's avatar
Richard M. Stallman committed
27 28
;;
;; Flyspell is a minor Emacs mode performing on-the-fly spelling
29
;; checking.
30
;;
31
;; To enable Flyspell minor mode, type M-x flyspell-mode.
32
;; This applies only to the current buffer.
33 34
;;
;; To enable Flyspell in text representing computer programs, type
35 36
;; M-x flyspell-prog-mode.
;; In that mode only text inside comments is checked.
37
;;
38
;; Note: consider setting the variable ispell-parser to `tex' to
39
;; avoid TeX command checking; use `(setq ispell-parser 'tex)'.
40
;;
Richard M. Stallman's avatar
Richard M. Stallman committed
41 42 43 44
;; Some user variables control the behavior of flyspell.  They are
;; those defined under the `User variables' comment.

;;; Code:
45

Richard M. Stallman's avatar
Richard M. Stallman committed
46 47
(require 'ispell)

48 49 50
;*---------------------------------------------------------------------*/
;*    Group ...                                                        */
;*---------------------------------------------------------------------*/
Richard M. Stallman's avatar
Richard M. Stallman committed
51
(defgroup flyspell nil
Gerd Moellmann's avatar
Gerd Moellmann committed
52
  "Spell checking on the fly."
Richard M. Stallman's avatar
Richard M. Stallman committed
53 54
  :tag "FlySpell"
  :prefix "flyspell-"
55
  :group 'ispell
56
  :group 'processes)
Richard M. Stallman's avatar
Richard M. Stallman committed
57

58 59 60 61 62 63 64 65 66 67 68
;*---------------------------------------------------------------------*/
;*    Which emacs are we currently running                             */
;*---------------------------------------------------------------------*/
(defvar flyspell-emacs
  (cond
   ((string-match "XEmacs" emacs-version)
    'xemacs)
   (t
    'emacs))
  "The type of Emacs we are currently running.")

Richard M. Stallman's avatar
Richard M. Stallman committed
69
;*---------------------------------------------------------------------*/
70
;*    User configuration ...                                           */
Richard M. Stallman's avatar
Richard M. Stallman committed
71 72
;*---------------------------------------------------------------------*/
(defcustom flyspell-highlight-flag t
73 74
  "*How Flyspell should indicate misspelled words.
Non-nil means use highlight, nil means use minibuffer messages."
Richard M. Stallman's avatar
Richard M. Stallman committed
75 76 77
  :group 'flyspell
  :type 'boolean)

78
(defcustom flyspell-mark-duplications-flag t
79
  "*Non-nil means Flyspell reports a repeated word as an error."
Richard M. Stallman's avatar
Richard M. Stallman committed
80 81 82
  :group 'flyspell
  :type 'boolean)

83
(defcustom flyspell-sort-corrections nil
Richard M. Stallman's avatar
Richard M. Stallman committed
84 85
  "*Non-nil means, sort the corrections alphabetically before popping them."
  :group 'flyspell
86
  :version "21.1"
Richard M. Stallman's avatar
Richard M. Stallman committed
87 88
  :type 'boolean)

89
(defcustom flyspell-duplicate-distance -1
90 91 92
  "*The maximum distance for finding duplicates of unrecognized words.
This applies to the feature that when a word is not found in the dictionary,
if the same spelling occurs elsewhere in the buffer,
93
Flyspell uses a different face (`flyspell-duplicate') to highlight it.
94
This variable specifies how far to search to find such a duplicate.
95
-1 means no limit (search the whole buffer).
96
0 means do not search for duplicate unrecognized spellings."
Richard M. Stallman's avatar
Richard M. Stallman committed
97
  :group 'flyspell
98
  :version "21.1"
Richard M. Stallman's avatar
Richard M. Stallman committed
99 100 101
  :type 'number)

(defcustom flyspell-delay 3
102
  "*The number of seconds to wait before checking, after a \"delayed\" command."
Richard M. Stallman's avatar
Richard M. Stallman committed
103 104 105 106
  :group 'flyspell
  :type 'number)

(defcustom flyspell-persistent-highlight t
107 108 109
  "*Non-nil means misspelled words remain highlighted until corrected.
If this variable is nil, only the most recently detected misspelled word
is highlighted."
Richard M. Stallman's avatar
Richard M. Stallman committed
110 111 112 113
  :group 'flyspell
  :type 'boolean)

(defcustom flyspell-highlight-properties t
114
  "*Non-nil means highlight incorrect words even if a property exists for this word."
Richard M. Stallman's avatar
Richard M. Stallman committed
115 116 117 118 119 120
  :group 'flyspell
  :type 'boolean)

(defcustom flyspell-default-delayed-commands
  '(self-insert-command
    delete-backward-char
121 122
    backward-or-forward-delete-char
    delete-char
123 124
    scrollbar-vertical-drag
    backward-delete-char-untabify)
125 126
  "The standard list of delayed commands for Flyspell.
See `flyspell-delayed-commands'."
Richard M. Stallman's avatar
Richard M. Stallman committed
127
  :group 'flyspell
128
  :version "21.1"
Richard M. Stallman's avatar
Richard M. Stallman committed
129 130
  :type '(repeat (symbol)))

131 132
(defcustom flyspell-delayed-commands nil
  "List of commands that are \"delayed\" for Flyspell mode.
133 134
After these commands, Flyspell checking is delayed for a short time,
whose length is specified by `flyspell-delay'."
Richard M. Stallman's avatar
Richard M. Stallman committed
135 136 137
  :group 'flyspell
  :type '(repeat (symbol)))

138 139 140 141 142 143 144 145
(defcustom flyspell-default-deplacement-commands
  '(next-line
    previous-line
    scroll-up
    scroll-down)
  "The standard list of deplacement commands for Flyspell.
See `flyspell-deplacement-commands'."
  :group 'flyspell
146
  :version "21.1"
147 148 149 150 151 152 153
  :type '(repeat (symbol)))

(defcustom flyspell-deplacement-commands nil
  "List of commands that are \"deplacement\" for Flyspell mode.
After these commands, Flyspell checking is performed only if the previous
command was not the very same command."
  :group 'flyspell
154
  :version "21.1"
155 156
  :type '(repeat (symbol)))

Richard M. Stallman's avatar
Richard M. Stallman committed
157
(defcustom flyspell-issue-welcome-flag t
158
  "*Non-nil means that Flyspell should display a welcome message when started."
Richard M. Stallman's avatar
Richard M. Stallman committed
159 160 161
  :group 'flyspell
  :type 'boolean)

162 163 164 165 166
(defcustom flyspell-issue-message-flag t
  "*Non-nil means that Flyspell emits messages when checking words."
  :group 'flyspell
  :type 'boolean)

167 168 169
(defcustom flyspell-incorrect-hook nil
  "*List of functions to be called when incorrect words are encountered.
Each function is given three arguments: the beginning and the end
170
of the incorrect region.  The third is either the symbol 'doublon' or the list
171
of possible corrections as returned by `ispell-parse-output'.
172

Gerd Moellmann's avatar
Gerd Moellmann committed
173
If any of the functions return non-Nil, the word is not highlighted as
174 175
incorrect."
  :group 'flyspell
176
  :version "21.1"
177 178
  :type 'hook)

179
(defcustom flyspell-default-dictionary nil
180
  "A string that is the name of the default dictionary.
181
This is passed to the `ispell-change-dictionary' when flyspell is started.
182 183 184 185 186
If the variable `ispell-local-dictionary' or `ispell-dictionary' is non-nil
when flyspell is started, the value of that variable is used instead
of `flyspell-default-dictionary' to select the default dictionary.
Otherwise, if `flyspell-default-dictionary' is nil, it means to use
Ispell's ultimate default dictionary."
187
  :group 'flyspell
188
  :version "21.1"
189
  :type '(choice string (const :tag "Default" nil)))
190 191 192 193 194

(defcustom flyspell-tex-command-regexp
  "\\(\\(begin\\|end\\)[ \t]*{\\|\\(cite[a-z*]*\\|label\\|ref\\|eqref\\|usepackage\\|documentclass\\)[ \t]*\\(\\[[^]]*\\]\\)?{[^{}]*\\)"
  "A string that is the regular expression that matches TeX commands."
  :group 'flyspell
195
  :version "21.1"
196 197 198
  :type 'string)

(defcustom flyspell-check-tex-math-command nil
Gerd Moellmann's avatar
Gerd Moellmann committed
199
  "*Non nil means check even inside TeX math environment.
200 201
TeX math environments are discovered by the TEXMATHP that implemented
inside the texmathp.el Emacs package.  That package may be found at:
202
http://strw.leidenuniv.nl/~dominik/Tools"
Richard M. Stallman's avatar
Richard M. Stallman committed
203 204 205
  :group 'flyspell
  :type 'boolean)

206 207 208 209
(defcustom flyspell-dictionaries-that-consider-dash-as-word-delimiter
  '("francais" "deutsch8" "norsk")
  "List of dictionary names that consider `-' as word delimiter."
  :group 'flyspell
210
  :version "21.1"
211
  :type '(repeat (string)))
Richard M. Stallman's avatar
Richard M. Stallman committed
212

213
(defcustom flyspell-abbrev-p
214 215
  nil
  "*If non-nil, add correction to abbreviation table."
Richard M. Stallman's avatar
Richard M. Stallman committed
216
  :group 'flyspell
217
  :version "21.1"
Richard M. Stallman's avatar
Richard M. Stallman committed
218 219
  :type 'boolean)

220 221
(defcustom flyspell-use-global-abbrev-table-p
  nil
222
  "*If non-nil, prefer global abbrev table to local abbrev table."
223
  :group 'flyspell
224
  :version "21.1"
225
  :type 'boolean)
226

227 228 229 230
(defcustom flyspell-mode-line-string " Fly"
  "*String displayed on the modeline when flyspell is active.
Set this to nil if you don't want a modeline indicator."
  :group 'flyspell
231
  :type '(choice string (const :tag "None" nil)))
232 233

(defcustom flyspell-large-region 1000
234
  "*The threshold that determines if a region is small.
235 236 237
If the region is smaller than this number of characters,
`flyspell-region' checks the words sequentially using regular
flyspell methods.  Else, if the region is large, a new Ispell process is
238 239 240
spawned for speed.

If `flyspell-large-region' is nil, all regions are treated as small."
241
  :group 'flyspell
242
  :version "21.1"
243
  :type '(choice number boolean))
244

245
(defcustom flyspell-insert-function (function insert)
246
  "*Function for inserting word by flyspell upon correction."
247 248 249 250 251 252 253 254 255 256 257 258 259
  :group 'flyspell
  :type 'function)

(defcustom flyspell-before-incorrect-word-string nil
  "String used to indicate an incorrect word starting."
  :group 'flyspell
  :type '(choice string (const nil)))

(defcustom flyspell-after-incorrect-word-string nil
  "String used to indicate an incorrect word ending."
  :group 'flyspell
  :type '(choice string (const nil)))

260 261 262 263 264 265
(defcustom flyspell-use-meta-tab t
  "*Non-nil means that flyspell uses META-TAB to correct word."
  :group 'flyspell
  :type 'boolean)

(defcustom flyspell-auto-correct-binding
266
  [(control ?\;)]
267 268 269
  "The key binding for flyspell auto correction."
  :group 'flyspell)

Richard M. Stallman's avatar
Richard M. Stallman committed
270 271 272 273 274 275 276 277 278 279 280 281 282
;*---------------------------------------------------------------------*/
;*    Mode specific options                                            */
;*    -------------------------------------------------------------    */
;*    Mode specific options enable users to disable flyspell on        */
;*    certain word depending of the emacs mode. For instance, when     */
;*    using flyspell with mail-mode add the following expression       */
;*    in your .emacs file:                                             */
;*       (add-hook 'mail-mode                                          */
;*    	     '(lambda () (setq flyspell-generic-check-word-p           */
;*    			       'mail-mode-flyspell-verify)))           */
;*---------------------------------------------------------------------*/
(defvar flyspell-generic-check-word-p nil
  "Function providing per-mode customization over which words are flyspelled.
283 284 285
Returns t to continue checking, nil otherwise.
Flyspell mode sets this variable to whatever is the `flyspell-mode-predicate'
property of the major mode name.")
Richard M. Stallman's avatar
Richard M. Stallman committed
286 287
(make-variable-buffer-local 'flyspell-generic-check-word-p)

288
;*--- mail mode -------------------------------------------------------*/
289 290
(put 'mail-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify)
(put 'message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify)
Richard M. Stallman's avatar
Richard M. Stallman committed
291
(defun mail-mode-flyspell-verify ()
292
  "This function is used for `flyspell-generic-check-word-p' in Mail mode."
293 294 295 296 297 298 299 300 301 302 303 304 305 306
  (let ((header-end (save-excursion
		      (goto-char (point-min))
		      (re-search-forward
		       (concat "^"
			       (regexp-quote mail-header-separator)
			       "$")
		       nil t)
		      (point)))
	(signature-begin (save-excursion
			   (goto-char (point-max))
			   (re-search-backward message-signature-separator
					       nil t)
			   (point))))
    (cond ((< (point) header-end)
307 308 309
	   (and (save-excursion (beginning-of-line)
				(looking-at "^Subject:"))
		(> (point) (match-end 0))))
310
	  ((> (point) signature-begin)
311 312 313 314
	   nil)
	  (t
	   (save-excursion
	     (beginning-of-line)
315
	     (not (looking-at "[>}|]\\|To:")))))))
Richard M. Stallman's avatar
Richard M. Stallman committed
316

317
;*--- texinfo mode ----------------------------------------------------*/
318
(put 'texinfo-mode 'flyspell-mode-predicate 'texinfo-mode-flyspell-verify)
Richard M. Stallman's avatar
Richard M. Stallman committed
319
(defun texinfo-mode-flyspell-verify ()
320
  "This function is used for `flyspell-generic-check-word-p' in Texinfo mode."
Richard M. Stallman's avatar
Richard M. Stallman committed
321 322 323 324
  (save-excursion
    (forward-word -1)
    (not (looking-at "@"))))

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
;*--- tex mode --------------------------------------------------------*/
(put 'tex-mode 'flyspell-mode-predicate 'tex-mode-flyspell-verify)
(defun tex-mode-flyspell-verify ()
  "This function is used for `flyspell-generic-check-word-p' in LaTeX mode."
  (and
   (not (save-excursion
	  (re-search-backward "^[ \t]*%%%[ \t]+Local" (point-min) t)))
   (not (save-excursion
	  (let ((this (point-marker))
		(e (progn (end-of-line) (point-marker))))
	    (beginning-of-line)
	    (if (re-search-forward "\\\\\\(cite\\|label\\|ref\\){[^}]*}" e t)
		(and (>= this (match-beginning 0))
		     (<= this (match-end 0)) )))))))

;*--- sgml mode -------------------------------------------------------*/
(put 'sgml-mode 'flyspell-mode-predicate 'sgml-mode-flyspell-verify)
(put 'html-mode 'flyspell-mode-predicate 'sgml-mode-flyspell-verify)

(defun sgml-mode-flyspell-verify ()
  "This function is used for `flyspell-generic-check-word-p' in SGML mode."
  (not (save-excursion
	 (let ((this (point-marker))
	       (s (progn (beginning-of-line) (point-marker)))
	       (e (progn (end-of-line) (point-marker))))
	   (or (progn
		 (goto-char this)
		 (and (re-search-forward  "[^<]*>" e t)
		      (= (match-beginning 0) this)))
	       (progn
		 (goto-char this)
		 (and (re-search-backward "<[^>]*" s t)
		      (= (match-end 0) this)))
	       (and (progn
		      (goto-char this)
		      (and (re-search-forward  "[^&]*;" e t)
			   (= (match-beginning 0) this)))
		    (progn
		      (goto-char this)
		      (and (re-search-backward "&[^;]*" s t)
			   (= (match-end 0) this)))))))))

;*---------------------------------------------------------------------*/
;*    Programming mode                                                 */
;*---------------------------------------------------------------------*/
370 371 372 373
(defvar flyspell-prog-text-faces
  '(font-lock-string-face font-lock-comment-face font-lock-doc-face)
  "Faces corresponding to text in programming-mode buffers.")

374 375 376
(defun flyspell-generic-progmode-verify ()
  "Used for `flyspell-generic-check-word-p' in programming modes."
  (let ((f (get-text-property (point) 'face)))
377
    (memq f flyspell-prog-text-faces)))
378 379 380 381 382 383

;;;###autoload
(defun flyspell-prog-mode ()
  "Turn on `flyspell-mode' for comments and strings."
  (interactive)
  (setq flyspell-generic-check-word-p 'flyspell-generic-progmode-verify)
384 385
  (flyspell-mode 1)
  (run-hooks 'flyspell-prog-mode-hook))
386

Richard M. Stallman's avatar
Richard M. Stallman committed
387 388 389
;*---------------------------------------------------------------------*/
;*    Overlay compatibility                                            */
;*---------------------------------------------------------------------*/
390 391 392 393 394 395 396 397
(autoload 'make-overlay            "overlay" "Overlay compatibility kit." t)
(autoload 'overlayp                "overlay" "Overlay compatibility kit." t)
(autoload 'overlays-in             "overlay" "Overlay compatibility kit." t)
(autoload 'delete-overlay          "overlay" "Overlay compatibility kit." t)
(autoload 'overlays-at             "overlay" "Overlay compatibility kit." t)
(autoload 'overlay-put             "overlay" "Overlay compatibility kit." t)
(autoload 'overlay-get             "overlay" "Overlay compatibility kit." t)
(autoload 'previous-overlay-change "overlay" "Overlay compatibility kit." t)
Richard M. Stallman's avatar
Richard M. Stallman committed
398 399 400 401

;*---------------------------------------------------------------------*/
;*    The minor mode declaration.                                      */
;*---------------------------------------------------------------------*/
402 403
(defvar flyspell-mouse-map
  (let ((map (make-sparse-keymap)))
404 405
    (define-key map (if (featurep 'xemacs) [button2] [down-mouse-2])
      #'flyspell-correct-word)
406 407
    map)
  "Keymap for Flyspell to put on erroneous words.")
Richard M. Stallman's avatar
Richard M. Stallman committed
408

409 410 411 412
(defvar flyspell-mode-map
  (let ((map (make-sparse-keymap)))
    (if flyspell-use-meta-tab
      (define-key map "\M-\t" 'flyspell-auto-correct-word))
413 414 415 416 417
    (define-key map flyspell-auto-correct-binding 'flyspell-auto-correct-previous-word)
    (define-key map [(control ?\,)] 'flyspell-goto-next-error)
    (define-key map [(control ?\.)] 'flyspell-auto-correct-word)
    map)
  "Minor mode keymap for Flyspell mode--for the whole buffer.")
418 419 420 421 422 423 424 425 426 427

;; dash character machinery
(defvar flyspell-consider-dash-as-word-delimiter-flag nil
   "*Non-nil means that the `-' char is considered as a word delimiter.")
(make-variable-buffer-local 'flyspell-consider-dash-as-word-delimiter-flag)
(defvar flyspell-dash-dictionary nil)
(make-variable-buffer-local 'flyspell-dash-dictionary)
(defvar flyspell-dash-local-dictionary nil)
(make-variable-buffer-local 'flyspell-dash-local-dictionary)

Richard M. Stallman's avatar
Richard M. Stallman committed
428 429 430
;*---------------------------------------------------------------------*/
;*    Highlighting                                                     */
;*---------------------------------------------------------------------*/
431
(defface flyspell-incorrect
432 433
  '((((class color)) (:foreground "OrangeRed" :bold t :underline t))
    (t (:bold t)))
434
  "Face used for marking a misspelled word in Flyspell."
435
  :group 'flyspell)
436 437
;; backward-compatibility alias
(put 'flyspell-incorrect-face 'face-alias 'flyspell-incorrect)
438

439
(defface flyspell-duplicate
440 441
  '((((class color)) (:foreground "Gold3" :bold t :underline t))
    (t (:bold t)))
442 443
  "Face used for marking a misspelled word that appears twice in the buffer.
See also `flyspell-duplicate-distance'."
444
  :group 'flyspell)
445 446
;; backward-compatibility alias
(put 'flyspell-duplicate-face 'face-alias 'flyspell-duplicate)
447

Richard M. Stallman's avatar
Richard M. Stallman committed
448 449 450 451 452 453
(defvar flyspell-overlay nil)

;*---------------------------------------------------------------------*/
;*    flyspell-mode ...                                                */
;*---------------------------------------------------------------------*/
;;;###autoload
454
(define-minor-mode flyspell-mode
Richard M. Stallman's avatar
Richard M. Stallman committed
455
  "Minor mode performing on-the-fly spelling checking.
456
This spawns a single Ispell process and checks each word.
457 458 459
The default flyspell behavior is to highlight incorrect words.
With no argument, this command toggles Flyspell mode.
With a prefix argument ARG, turn Flyspell minor mode on iff ARG is positive.
460

Richard M. Stallman's avatar
Richard M. Stallman committed
461 462 463
Bindings:
\\[ispell-word]: correct words (using Ispell).
\\[flyspell-auto-correct-word]: automatically correct word.
464 465
\\[flyspell-auto-correct-previous-word]: automatically correct the last misspelled word.
\\[flyspell-correct-word] (or down-mouse-2): popup correct words.
Richard M. Stallman's avatar
Richard M. Stallman committed
466 467

Hooks:
468
This runs `flyspell-mode-hook' after flyspell is entered.
Richard M. Stallman's avatar
Richard M. Stallman committed
469 470 471 472 473 474 475 476

Remark:
`flyspell-mode' uses `ispell-mode'.  Thus all Ispell options are
valid.  For instance, a personal dictionary can be used by
invoking `ispell-change-dictionary'.

Consider using the `ispell-parser' to check your text.  For instance
consider adding:
477
\(add-hook 'tex-mode-hook (function (lambda () (setq ispell-parser 'tex))))
Richard M. Stallman's avatar
Richard M. Stallman committed
478 479
in your .emacs file.

480 481
\\[flyspell-region] checks all words inside a region.
\\[flyspell-buffer] checks the whole buffer."
482 483 484 485 486 487
  :lighter flyspell-mode-line-string
  :keymap flyspell-mode-map
  :group 'flyspell
  (if flyspell-mode
      (flyspell-mode-on)
    (flyspell-mode-off)))
488 489 490 491 492 493 494

;*---------------------------------------------------------------------*/
;*    flyspell-buffers ...                                             */
;*    -------------------------------------------------------------    */
;*    For remembering buffers running flyspell                         */
;*---------------------------------------------------------------------*/
(defvar flyspell-buffers nil)
495

496 497 498 499 500 501 502 503 504 505 506 507
;*---------------------------------------------------------------------*/
;*    flyspell-minibuffer-p ...                                        */
;*---------------------------------------------------------------------*/
(defun flyspell-minibuffer-p (buffer)
  "Is BUFFER a minibuffer?"
  (let ((ws (get-buffer-window-list buffer t)))
    (and (consp ws) (window-minibuffer-p (car ws)))))

;*---------------------------------------------------------------------*/
;*    flyspell-accept-buffer-local-defs ...                            */
;*---------------------------------------------------------------------*/
(defun flyspell-accept-buffer-local-defs ()
508 509 510 511 512 513 514
  ;; strange problem.  If buffer in current window has font-lock turned on,
  ;; but SET-BUFFER was called to point to an invisible buffer, this ispell
  ;; call will reset the buffer to the buffer in the current window.  However,
  ;; it only happens at startup (fix by Albert L. Ting).
  (let ((buf (current-buffer)))
    (ispell-accept-buffer-local-defs)
    (set-buffer buf))
515 516
  (if (not (and (eq flyspell-dash-dictionary ispell-dictionary)
		(eq flyspell-dash-local-dictionary ispell-local-dictionary)))
517
      ;; The dictionary has changed
518 519 520 521 522 523 524 525
      (progn
	(setq flyspell-dash-dictionary ispell-dictionary)
	(setq flyspell-dash-local-dictionary ispell-local-dictionary)
	(if (member (or ispell-local-dictionary ispell-dictionary)
		    flyspell-dictionaries-that-consider-dash-as-word-delimiter)
	    (setq flyspell-consider-dash-as-word-delimiter-flag t)
	  (setq flyspell-consider-dash-as-word-delimiter-flag nil)))))

Richard M. Stallman's avatar
Richard M. Stallman committed
526 527 528 529
;*---------------------------------------------------------------------*/
;*    flyspell-mode-on ...                                             */
;*---------------------------------------------------------------------*/
(defun flyspell-mode-on ()
Karl Heuer's avatar
Karl Heuer committed
530
  "Turn Flyspell mode on.  Do not use this; use `flyspell-mode' instead."
531
  (setq ispell-highlight-face 'flyspell-incorrect)
532
  ;; local dictionaries setup
533 534 535
  (or ispell-local-dictionary ispell-dictionary
      (if flyspell-default-dictionary
	  (ispell-change-dictionary flyspell-default-dictionary)))
536 537 538 539
  ;; we have to force ispell to accept the local definition or
  ;; otherwise it could be too late, the local dictionary may
  ;; be forgotten!
  (flyspell-accept-buffer-local-defs)
540
  ;; we put the `flyspell-delayed' property on some commands
Richard M. Stallman's avatar
Richard M. Stallman committed
541
  (flyspell-delay-commands)
542
  ;; we put the `flyspell-deplacement' property on some commands
543
  (flyspell-deplacement-commands)
Richard M. Stallman's avatar
Richard M. Stallman committed
544
  ;; we bound flyspell action to post-command hook
545
  (add-hook 'post-command-hook (function flyspell-post-command-hook) t t)
Richard M. Stallman's avatar
Richard M. Stallman committed
546
  ;; we bound flyspell action to pre-command hook
547
  (add-hook 'pre-command-hook (function flyspell-pre-command-hook) t t)
548 549 550 551 552
  ;; we bound flyspell action to after-change hook
  (make-local-variable 'after-change-functions)
  (setq after-change-functions
	(cons 'flyspell-after-change-function after-change-functions))
  ;; set flyspell-generic-check-word-p based on the major mode
553 554 555
  (let ((mode-predicate (get major-mode 'flyspell-mode-predicate)))
    (if mode-predicate
	(setq flyspell-generic-check-word-p mode-predicate)))
Richard M. Stallman's avatar
Richard M. Stallman committed
556
  ;; the welcome message
557 558 559
  (if (and flyspell-issue-message-flag
	   flyspell-issue-welcome-flag
	   (interactive-p))
560 561 562 563
      (let ((binding (where-is-internal 'flyspell-auto-correct-word
					nil 'non-ascii)))
	(message
	 (if binding
564
	     (format "Welcome to flyspell. Use %s or Mouse-2 to correct words."
565
		     (key-description binding))
566
	   "Welcome to flyspell. Use Mouse-2 to correct words."))))
Richard M. Stallman's avatar
Richard M. Stallman committed
567 568 569 570 571 572 573
  ;; we end with the flyspell hooks
  (run-hooks 'flyspell-mode-hook))

;*---------------------------------------------------------------------*/
;*    flyspell-delay-commands ...                                      */
;*---------------------------------------------------------------------*/
(defun flyspell-delay-commands ()
Karl Heuer's avatar
Karl Heuer committed
574
  "Install the standard set of Flyspell delayed commands."
Richard M. Stallman's avatar
Richard M. Stallman committed
575 576 577 578 579 580 581
  (mapcar 'flyspell-delay-command flyspell-default-delayed-commands)
  (mapcar 'flyspell-delay-command flyspell-delayed-commands))

;*---------------------------------------------------------------------*/
;*    flyspell-delay-command ...                                       */
;*---------------------------------------------------------------------*/
(defun flyspell-delay-command (command)
Karl Heuer's avatar
Karl Heuer committed
582
  "Set COMMAND to be delayed, for Flyspell.
Richard M. Stallman's avatar
Richard M. Stallman committed
583
When flyspell `post-command-hook' is invoked because a delayed command
Gerd Moellmann's avatar
Gerd Moellmann committed
584
as been used the current word is not immediately checked.
585 586
It will be checked only after `flyspell-delay' seconds."
  (interactive "SDelay Flyspell after Command: ")
Richard M. Stallman's avatar
Richard M. Stallman committed
587 588 589
  (put command 'flyspell-delayed t))

;*---------------------------------------------------------------------*/
590
;*    flyspell-deplacement-commands ...                                */
Richard M. Stallman's avatar
Richard M. Stallman committed
591
;*---------------------------------------------------------------------*/
592 593 594 595
(defun flyspell-deplacement-commands ()
  "Install the standard set of Flyspell deplacement commands."
  (mapcar 'flyspell-deplacement-command flyspell-default-deplacement-commands)
  (mapcar 'flyspell-deplacement-command flyspell-deplacement-commands))
Richard M. Stallman's avatar
Richard M. Stallman committed
596 597

;*---------------------------------------------------------------------*/
598
;*    flyspell-deplacement-command ...                                 */
Richard M. Stallman's avatar
Richard M. Stallman committed
599
;*---------------------------------------------------------------------*/
600 601 602 603 604 605 606
(defun flyspell-deplacement-command (command)
  "Set COMMAND that implement cursor movements, for Flyspell.
When flyspell `post-command-hook' is invoked because of a deplacement command
as been used the current word is checked only if the previous command was
not the very same deplacement command."
  (interactive "SDeplacement Flyspell after Command: ")
  (put command 'flyspell-deplacement t))
Richard M. Stallman's avatar
Richard M. Stallman committed
607 608 609 610 611 612 613

;*---------------------------------------------------------------------*/
;*    flyspell-word-cache ...                                          */
;*---------------------------------------------------------------------*/
(defvar flyspell-word-cache-start  nil)
(defvar flyspell-word-cache-end    nil)
(defvar flyspell-word-cache-word   nil)
614
(defvar flyspell-word-cache-result '_)
Richard M. Stallman's avatar
Richard M. Stallman committed
615 616 617
(make-variable-buffer-local 'flyspell-word-cache-start)
(make-variable-buffer-local 'flyspell-word-cache-end)
(make-variable-buffer-local 'flyspell-word-cache-word)
618
(make-variable-buffer-local 'flyspell-word-cache-result)
Richard M. Stallman's avatar
Richard M. Stallman committed
619 620 621 622 623 624

;*---------------------------------------------------------------------*/
;*    The flyspell pre-hook, store the current position. In the        */
;*    post command hook, we will check, if the word at this position   */
;*    has to be spell checked.                                         */
;*---------------------------------------------------------------------*/
625 626 627 628 629 630 631 632 633 634 635
(defvar flyspell-pre-buffer     nil)
(defvar flyspell-pre-point      nil)
(defvar flyspell-pre-column     nil)
(defvar flyspell-pre-pre-buffer nil)
(defvar flyspell-pre-pre-point  nil)

;*---------------------------------------------------------------------*/
;*    flyspell-previous-command ...                                    */
;*---------------------------------------------------------------------*/
(defvar flyspell-previous-command nil
  "The last interactive command checked by Flyspell.")
Richard M. Stallman's avatar
Richard M. Stallman committed
636 637 638 639 640

;*---------------------------------------------------------------------*/
;*    flyspell-pre-command-hook ...                                    */
;*---------------------------------------------------------------------*/
(defun flyspell-pre-command-hook ()
641
  "Save the current buffer and point for Flyspell's post-command hook."
Richard M. Stallman's avatar
Richard M. Stallman committed
642 643
  (interactive)
  (setq flyspell-pre-buffer (current-buffer))
644 645
  (setq flyspell-pre-point  (point))
  (setq flyspell-pre-column (current-column)))
Richard M. Stallman's avatar
Richard M. Stallman committed
646 647 648 649

;*---------------------------------------------------------------------*/
;*    flyspell-mode-off ...                                            */
;*---------------------------------------------------------------------*/
Karl Heuer's avatar
Karl Heuer committed
650
;;;###autoload
Richard M. Stallman's avatar
Richard M. Stallman committed
651
(defun flyspell-mode-off ()
Karl Heuer's avatar
Karl Heuer committed
652
  "Turn Flyspell mode off."
Richard M. Stallman's avatar
Richard M. Stallman committed
653
  ;; we remove the hooks
654 655
  (remove-hook 'post-command-hook (function flyspell-post-command-hook) t)
  (remove-hook 'pre-command-hook (function flyspell-pre-command-hook) t)
656 657
  (setq after-change-functions (delq 'flyspell-after-change-function
				     after-change-functions))
Richard M. Stallman's avatar
Richard M. Stallman committed
658 659 660 661 662 663 664 665 666 667 668 669
  ;; we remove all the flyspell hilightings
  (flyspell-delete-all-overlays)
  ;; we have to erase pre cache variables
  (setq flyspell-pre-buffer nil)
  (setq flyspell-pre-point  nil)
  ;; we mark the mode as killed
  (setq flyspell-mode nil))

;*---------------------------------------------------------------------*/
;*    flyspell-check-pre-word-p ...                                    */
;*---------------------------------------------------------------------*/
(defun flyspell-check-pre-word-p ()
670
  "Return non-nil if we should check the word before point.
671 672
More precisely, it applies to the word that was before point
before the current command."
Richard M. Stallman's avatar
Richard M. Stallman committed
673 674 675 676 677
  (cond
   ((or (not (numberp flyspell-pre-point))
	(not (bufferp flyspell-pre-buffer))
	(not (buffer-live-p flyspell-pre-buffer)))
    nil)
678 679 680
   ((and (eq flyspell-pre-pre-point flyspell-pre-point)
	 (eq flyspell-pre-pre-buffer flyspell-pre-buffer))
    nil)
681 682
   ((or (and (= flyspell-pre-point (- (point) 1))
	     (eq (char-syntax (char-after flyspell-pre-point)) ?w))
Richard M. Stallman's avatar
Richard M. Stallman committed
683 684 685
	(= flyspell-pre-point (point))
	(= flyspell-pre-point (+ (point) 1)))
    nil)
686
   ((and (symbolp this-command)
687
	 (not executing-kbd-macro)
688 689 690 691 692 693 694
	 (or (get this-command 'flyspell-delayed)
	     (and (get this-command 'flyspell-deplacement)
		  (eq flyspell-previous-command this-command)))
	 (or (= (current-column) 0)
	     (= (current-column) flyspell-pre-column)
	     (eq (char-syntax (char-after flyspell-pre-point)) ?w)))
    nil)
Richard M. Stallman's avatar
Richard M. Stallman committed
695 696 697 698 699 700 701 702
   ((not (eq (current-buffer) flyspell-pre-buffer))
    t)
   ((not (and (numberp flyspell-word-cache-start)
	      (numberp flyspell-word-cache-end)))
    t)
   (t
    (or (< flyspell-pre-point flyspell-word-cache-start)
	(> flyspell-pre-point flyspell-word-cache-end)))))
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726

;*---------------------------------------------------------------------*/
;*    The flyspell after-change-hook, store the change position. In    */
;*    the post command hook, we will check, if the word at this        */
;*    position has to be spell checked.                                */
;*---------------------------------------------------------------------*/
(defvar flyspell-changes nil)

;*---------------------------------------------------------------------*/
;*    flyspell-after-change-function ...                               */
;*---------------------------------------------------------------------*/
(defun flyspell-after-change-function (start stop len)
  "Save the current buffer and point for Flyspell's post-command hook."
  (interactive)
  (setq flyspell-changes (cons (cons start stop) flyspell-changes)))

;*---------------------------------------------------------------------*/
;*    flyspell-check-changed-word-p ...                                */
;*---------------------------------------------------------------------*/
(defun flyspell-check-changed-word-p (start stop)
  "Return t when the changed word has to be checked.
The answer depends of several criteria.
Mostly we check word delimiters."
  (cond
727
   ((and (memq (char-after start) '(?\n ? )) (> stop start))
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
    t)
   ((not (numberp flyspell-pre-point))
    t)
   ((and (>= flyspell-pre-point start) (<= flyspell-pre-point stop))
    nil)
   ((let ((pos (point)))
      (or (>= pos start) (<= pos stop) (= pos (1+ stop))))
    nil)
   (t
    t)))

;*---------------------------------------------------------------------*/
;*    flyspell-check-word-p ...                                        */
;*---------------------------------------------------------------------*/
(defun flyspell-check-word-p ()
  "Return t when the word at `point' has to be checked.
The answer depends of several criteria.
Mostly we check word delimiters."
  (cond
   ((<= (- (point-max) 1) (point-min))
    ;; the buffer is not filled enough
    nil)
   ((and (and (> (current-column) 0)
	      (not (eq (current-column) flyspell-pre-column)))
	 (save-excursion
	   (backward-char 1)
	   (and (looking-at (flyspell-get-not-casechars))
		(or flyspell-consider-dash-as-word-delimiter-flag
		    (not (looking-at "\\-"))))))
    ;; yes because we have reached or typed a word delimiter.
    t)
   ((symbolp this-command)
    (cond
     ((get this-command 'flyspell-deplacement)
      (not (eq flyspell-previous-command this-command)))
     ((get this-command 'flyspell-delayed)
      ;; the current command is not delayed, that
      ;; is that we must check the word now
766
      (sit-for flyspell-delay))
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
     (t t)))
   (t t)))

;*---------------------------------------------------------------------*/
;*    flyspell-debug-signal-no-check ...                               */
;*---------------------------------------------------------------------*/
(defun flyspell-debug-signal-no-check (msg obj)
  (setq debug-on-error t)
  (save-excursion
    (let ((buffer (get-buffer-create "*flyspell-debug*")))
      (set-buffer buffer)
      (erase-buffer)
      (insert "NO-CHECK:\n")
      (insert (format "    %S : %S\n" msg obj)))))

;*---------------------------------------------------------------------*/
783
;*    flyspell-debug-signal-pre-word-checked ...                       */
784 785 786 787 788 789 790 791 792 793 794 795
;*---------------------------------------------------------------------*/
(defun flyspell-debug-signal-pre-word-checked ()
  (setq debug-on-error t)
  (save-excursion
    (let ((buffer (get-buffer-create "*flyspell-debug*")))
      (set-buffer buffer)
      (insert "PRE-WORD:\n")
      (insert (format "  pre-point  : %S\n" flyspell-pre-point))
      (insert (format "  pre-buffer : %S\n" flyspell-pre-buffer))
      (insert (format "  cache-start: %S\n" flyspell-word-cache-start))
      (insert (format "  cache-end  : %S\n" flyspell-word-cache-end))
      (goto-char (point-max)))))
796

797
;*---------------------------------------------------------------------*/
798
;*    flyspell-debug-signal-word-checked ...                           */
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
;*---------------------------------------------------------------------*/
(defun flyspell-debug-signal-word-checked ()
  (setq debug-on-error t)
  (save-excursion
    (let ((oldbuf (current-buffer))
	  (buffer (get-buffer-create "*flyspell-debug*"))
	  (point  (point)))
      (set-buffer buffer)
      (insert "WORD:\n")
      (insert (format "  this-cmd   : %S\n" this-command))
      (insert (format "  delayed    : %S\n" (and (symbolp this-command)
						 (get this-command 'flyspell-delayed))))
      (insert (format "  point      : %S\n" point))
      (insert (format "  prev-char  : [%c] %S\n"
		      (progn
			(set-buffer oldbuf)
			(let ((c (if (> (point) (point-min))
				     (save-excursion
				       (backward-char 1)
				       (char-after (point)))
				   ? )))
			  (set-buffer buffer)
			  c))
		      (progn
			(set-buffer oldbuf)
			(let ((c (if (> (point) (point-min))
				     (save-excursion
				       (backward-char 1)
				       (and (and (looking-at (flyspell-get-not-casechars)) 1)
					    (and (or flyspell-consider-dash-as-word-delimiter-flag
						     (not (looking-at "\\-"))) 2))))))
			  (set-buffer buffer)
			  c))))
      (insert (format "  because    : %S\n"
		      (cond
		       ((not (and (symbolp this-command)
				  (get this-command 'flyspell-delayed)))
			;; the current command is not delayed, that
			;; is that we must check the word now
			'not-delayed)
		       ((progn
			  (set-buffer oldbuf)
			  (let ((c (if (> (point) (point-min))
				       (save-excursion
					 (backward-char 1)
					 (and (looking-at (flyspell-get-not-casechars))
					      (or flyspell-consider-dash-as-word-delimiter-flag
						  (not (looking-at "\\-"))))))))
			    (set-buffer buffer)
			    c))
			;; yes because we have reached or typed a word delimiter.
			'separator)
		       ((not (integerp flyspell-delay))
			;; yes because the user had set up a no-delay configuration.
			'no-delay)
		       (t
			'sit-for))))
      (goto-char (point-max)))))

;*---------------------------------------------------------------------*/
859
;*    flyspell-debug-signal-changed-checked ...                        */
860 861 862 863 864 865 866 867 868 869 870
;*---------------------------------------------------------------------*/
(defun flyspell-debug-signal-changed-checked ()
  (setq debug-on-error t)
  (save-excursion
    (let ((buffer (get-buffer-create "*flyspell-debug*"))
	  (point  (point)))
      (set-buffer buffer)
      (insert "CHANGED WORD:\n")
      (insert (format "  point   : %S\n" point))
      (goto-char (point-max)))))

Richard M. Stallman's avatar
Richard M. Stallman committed
871 872
;*---------------------------------------------------------------------*/
;*    flyspell-post-command-hook ...                                   */
873 874 875 876 877 878 879 880 881 882 883
;*    -------------------------------------------------------------    */
;*    It is possible that we check several words:                      */
;*    1- the current word is checked if the predicate                  */
;*       FLYSPELL-CHECK-WORD-P is true                                 */
;*    2- the word that used to be the current word before the          */
;*       THIS-COMMAND is checked if:                                   */
;*        a- the previous word is different from the current word      */
;*        b- the previous word as not just been checked by the         */
;*           previous FLYSPELL-POST-COMMAND-HOOK                       */
;*    3- the words changed by the THIS-COMMAND that are neither the    */
;*       previous word nor the current word                            */
Richard M. Stallman's avatar
Richard M. Stallman committed
884 885 886 887
;*---------------------------------------------------------------------*/
(defun flyspell-post-command-hook ()
  "The `post-command-hook' used by flyspell to check a word in-the-fly."
  (interactive)
888 889
  (let ((command this-command))
    (if (flyspell-check-pre-word-p)
Richard M. Stallman's avatar
Richard M. Stallman committed
890
	(save-excursion
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
	  '(flyspell-debug-signal-pre-word-checked)
	  (set-buffer flyspell-pre-buffer)
	  (save-excursion
	    (goto-char flyspell-pre-point)
	    (flyspell-word))))
    (if (flyspell-check-word-p)
	(progn
	  '(flyspell-debug-signal-word-checked)
	  (flyspell-word)
	  ;; we remember which word we have just checked.
	  ;; this will be used next time we will check a word
	  ;; to compare the next current word with the word
	  ;; that as been registered in the pre-command-hook
	  ;; that is these variables are used within the predicate
	  ;; FLYSPELL-CHECK-PRE-WORD-P
	  (setq flyspell-pre-pre-buffer (current-buffer))
	  (setq flyspell-pre-pre-point  (point)))
      (progn
	(setq flyspell-pre-pre-buffer nil)
	(setq flyspell-pre-pre-point  nil)
	;; when a word is not checked because of a delayed command
	;; we do not disable the ispell cache.
	(if (and (symbolp this-command) (get this-command 'flyspell-delayed))
914 915 916
	    (progn
	      (setq flyspell-word-cache-end -1)
	      (setq flyspell-word-cache-result '_)))))
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
    (while (consp flyspell-changes)
      (let ((start (car (car flyspell-changes)))
	    (stop  (cdr (car flyspell-changes))))
	(if (flyspell-check-changed-word-p start stop)
	    (save-excursion
	      '(flyspell-debug-signal-changed-checked)
	      (goto-char start)
	      (flyspell-word)))
	(setq flyspell-changes (cdr flyspell-changes))))
    (setq flyspell-previous-command command)))

;*---------------------------------------------------------------------*/
;*    flyspell-notify-misspell ...                                     */
;*---------------------------------------------------------------------*/
(defun flyspell-notify-misspell (start end word poss)
  (let ((replacements (if (stringp poss)
			  poss
			(if flyspell-sort-corrections
			    (sort (car (cdr (cdr poss))) 'string<)
			  (car (cdr (cdr poss)))))))
937 938
    (if flyspell-issue-message-flag
	(message (format "mispelling `%s'  %S" word replacements)))))
Richard M. Stallman's avatar
Richard M. Stallman committed
939

940 941 942 943 944 945 946 947 948 949 950 951 952
;*---------------------------------------------------------------------*/
;*    flyspell-word-search-backward ...                                */
;*---------------------------------------------------------------------*/
(defun flyspell-word-search-backward (word bound)
  (save-excursion
    (let ((r '())
	  p)
      (while (and (not r) (setq p (search-backward word bound t)))
	(let ((lw (flyspell-get-word '())))
	  (if (and (consp lw) (string-equal (car lw) word))
	      (setq r p)
	    (goto-char p))))
      r)))
953

954 955 956 957 958 959 960 961 962 963 964 965 966
;*---------------------------------------------------------------------*/
;*    flyspell-word-search-forward ...                                 */
;*---------------------------------------------------------------------*/
(defun flyspell-word-search-forward (word bound)
  (save-excursion
    (let ((r '())
	  p)
      (while (and (not r) (setq p (search-forward word bound t)))
	(let ((lw (flyspell-get-word '())))
	  (if (and (consp lw) (string-equal (car lw) word))
	      (setq r p)
	    (goto-char (1+ p)))))
      r)))
967

Richard M. Stallman's avatar
Richard M. Stallman committed
968 969 970 971 972
;*---------------------------------------------------------------------*/
;*    flyspell-word ...                                                */
;*---------------------------------------------------------------------*/
(defun flyspell-word (&optional following)
  "Spell check a word."
973
  (interactive (list ispell-following-word))
Richard M. Stallman's avatar
Richard M. Stallman committed
974
  (save-excursion
975
    ;; use the correct dictionary
976
    (flyspell-accept-buffer-local-defs)
977 978 979 980
    (let* ((cursor-location (point))
	  (flyspell-word (flyspell-get-word following))
	  start end poss word)
      (if (or (eq flyspell-word nil)
Richard M. Stallman's avatar
Richard M. Stallman committed
981 982
 	      (and (fboundp flyspell-generic-check-word-p)
 		   (not (funcall flyspell-generic-check-word-p))))
983
	  t
Richard M. Stallman's avatar
Richard M. Stallman committed
984
	(progn
985 986 987 988
	  ;; destructure return flyspell-word info list.
	  (setq start (car (cdr flyspell-word))
		end (car (cdr (cdr flyspell-word)))
		word (car flyspell-word))
Richard M. Stallman's avatar
Richard M. Stallman committed
989 990
	  ;; before checking in the directory, we check for doublons.
	  (cond
991
	   ((and (or (not (eq ispell-parser 'tex))
992
		     (and (> start (point-min))
993
			  (not (memq (char-after (1- start)) '(?\} ?\\)))))
994
		 flyspell-mark-duplications-flag
Richard M. Stallman's avatar
Richard M. Stallman committed
995
		 (save-excursion
996
		   (goto-char (1- start))
997
		   (let ((p (flyspell-word-search-backward
998 999 1000
			     word
			     (- start (1+ (- end start))))))
		     (and p (/= p (1- start))))))
Richard M. Stallman's avatar
Richard M. Stallman committed
1001
	    ;; yes, this is a doublon
1002 1003
	    (flyspell-highlight-incorrect-region start end 'doublon)
	    nil)
Richard M. Stallman's avatar
Richard M. Stallman committed
1004 1005 1006 1007
	   ((and (eq flyspell-word-cache-start start)
		 (eq flyspell-word-cache-end end)
		 (string-equal flyspell-word-cache-word word))
	    ;; this word had been already checked, we skip
1008
	    flyspell-word-cache-result)
Richard M. Stallman's avatar
Richard M. Stallman committed
1009
	   ((and (eq ispell-parser 'tex)
1010
		 (flyspell-tex-command-p flyspell-word))
Richard M. Stallman's avatar
Richard M. Stallman committed
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
	    ;; this is a correct word (because a tex command)
	    (flyspell-unhighlight-at start)
	    (if (> end start)
		(flyspell-unhighlight-at (- end 1)))
	    t)
	   (t
	    ;; we setup the cache
	    (setq flyspell-word-cache-start start)
	    (setq flyspell-word-cache-end end)
	    (setq flyspell-word-cache-word word)
	    ;; now check spelling of word.
	    (process-send-string ispell-process "%\n")
	    ;; put in verbose mode
	    (process-send-string ispell-process
				 (concat "^" word "\n"))
1026 1027
	    ;; we mark the ispell process so it can be killed
	    ;; when emacs is exited without query
1028
	    (set-process-query-on-exit-flag ispell-process nil)
Richard M. Stallman's avatar
Richard M. Stallman committed
1029 1030 1031 1032 1033 1034 1035
	    ;; wait until ispell has processed word
	    (while (progn
		     (accept-process-output ispell-process)
		     (not (string= "" (car ispell-filter)))))
	    ;; (process-send-string ispell-process "!\n")
	    ;; back to terse mode.
	    (setq ispell-filter (cdr ispell-filter))
1036
	    (if (consp ispell-filter)
Richard M. Stallman's avatar
Richard M. Stallman committed
1037
		(setq poss (ispell-parse-output (car ispell-filter))))
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
	    (let ((res (cond ((eq poss t)
			      ;; correct
			      (setq flyspell-word-cache-result t)
			      (flyspell-unhighlight-at start)
			      (if (> end start)
				  (flyspell-unhighlight-at (- end 1)))
			      t)
			     ((and (stringp poss) flyspell-highlight-flag)
			      ;; correct
			      (setq flyspell-word-cache-result t)
			      (flyspell-unhighlight-at start)
			      (if (> end start)
				  (flyspell-unhighlight-at (- end 1)))
			      t)
			     ((null poss)
			      (setq flyspell-word-cache-result t)
			      (flyspell-unhighlight-at start)
			      (if (> end start)
				  (flyspell-unhighlight-at (- end 1)))
			      t)
			     ((or (and (< flyspell-duplicate-distance 0)
				       (or (save-excursion
					     (goto-char start)
					     (flyspell-word-search-backward
					      word
					      (point-min)))
					   (save-excursion
					     (goto-char end)
					     (flyspell-word-search-forward
					      word
					      (point-max)))))
				  (and (> flyspell-duplicate-distance 0)
				       (or (save-excursion
					     (goto-char start)
					     (flyspell-word-search-backward
					      word
					      (- start
						 flyspell-duplicate-distance)))
					   (save-excursion
					     (goto-char end)
					     (flyspell-word-search-forward
					      word
					      (+ end
						 flyspell-duplicate-distance))))))
			      (setq flyspell-word-cache-result nil)
			      (if flyspell-highlight-flag
				  (flyspell-highlight-duplicate-region
				   start end poss)
				(message (format "duplicate `%s'" word)))
			      nil)
			     (t
			      (setq flyspell-word-cache-result nil)
			      ;; incorrect highlight the location
			      (if flyspell-highlight-flag
				  (flyspell-highlight-incorrect-region
				   start end poss)
				(flyspell-notify-misspell start end word poss))
			      nil))))
	      ;; return to original location
1097
	      (goto-char cursor-location)