22611
|
1 ;;; flyspell.el --- On-the-fly spell checker
|
|
2
|
|
3 ;; Copyright (C) 1998 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Manuel Serrano <Manuel.Serrano@unice.fr>
|
|
6 ;; version 1.2h
|
|
7 ;; new version may be found at:
|
|
8 ;;
|
|
9 ;; http://kaolin.unice.fr/~serrano
|
|
10
|
|
11 ;;; This file is part of GNU Emacs.
|
|
12
|
|
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
14 ;; it under the terms of the GNU General Public License as published by
|
|
15 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
16 ;; any later version.
|
|
17
|
|
18 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
21 ;; GNU General Public License for more details.
|
|
22
|
|
23 ;; You should have received a copy of the GNU General Public License
|
|
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
26 ;; Boston, MA 02111-1307, USA.
|
|
27
|
|
28 ;;; commentary:
|
|
29 ;;
|
|
30 ;; Flyspell is a minor Emacs mode performing on-the-fly spelling
|
|
31 ;; checking. It requires `font-lock' and `ispell'. It has been
|
|
32 ;; tested on gnu-emacs 19.29, 19.34 and Xemacs 19.15.
|
|
33 ;;
|
|
34 ;; To install it, copy the flyspell.el file in your Emacs path and
|
|
35 ;; add to your .emacs file:
|
|
36 ;; `(autoload 'flyspell-mode "flyspell" "On-the-fly Ispell." t)'
|
|
37 ;;
|
|
38 ;; To enter the flyspell minor mode, Meta-x flyspell-mode.
|
|
39 ;;
|
|
40 ;; Note: consider setting the variable ispell-parser to 'tex to
|
|
41 ;; avoid TeX command checking (use `(setq ispell-parser 'tex)')
|
|
42 ;; _before_ entering flyspell.
|
|
43 ;;
|
|
44 ;; Some user variables control the behavior of flyspell. They are
|
|
45 ;; those defined under the `User variables' comment.
|
|
46 ;;
|
|
47 ;; Note: as suggested by Yaron M. Minsky, if you use flyspell when
|
|
48 ;; sending mails, you should add the following:
|
|
49 ;; (add-hook 'mail-send-hook 'flyspell-mode-off)
|
|
50 ;; -------------------------------------------------------------
|
|
51 ;; Release 1.2h:
|
|
52 ;; - Fix a bug on mouse-2 (yank-at-click) for gnu-emacs.
|
|
53 ;; Release 1.2g:
|
|
54 ;; - Support for flyspell-generic-check-word-p (has suggested
|
|
55 ;; by Eric M. Ludlam).
|
|
56 ;; - Compliance to emacs-lisp comments.
|
|
57 ;; Release 1.2f:
|
|
58 ;; - Improved TeX handling.
|
|
59 ;; - Improved word fetch implementation.
|
|
60 ;; - flyspell-sort-corrections was not used inside
|
|
61 ;; flyspell-auto-correct-word. The consequence was that auto
|
|
62 ;; corrections where not sorted even if the variable was set
|
|
63 ;; to non-nil.
|
|
64 ;; - Support for flyspell-multi-language-p variable. Setting
|
|
65 ;; this variable to nil will prevent flyspell to spawn a new
|
|
66 ;; Ispell process per buffer.
|
|
67 ;; Release 1.2e:
|
|
68 ;; - Fix two popup bugs on Xemacs. If no replacement words are
|
|
69 ;; proposed only the save option is available. Corrected words
|
|
70 ;; were not inserted at the correct position in the buffer.
|
|
71 ;; - Addition of flyspell-region and flyspell-buffer.
|
|
72 ;; Release 1.2d:
|
|
73 ;; - Make-face-... expressions are now enclosed in
|
|
74 ;; condition-case expressions.
|
|
75 ;; - Fix bugs when flyspell-auto-correct-binding is set to nil
|
|
76 ;; (thanks to Eli Tziperman).
|
|
77 ;; Release 1.2c:
|
|
78 ;; - Fix the overlay keymap problem for Emacs (it was correctly
|
|
79 ;; working with Xemacs).
|
|
80 ;; - Thanks to Didier Remy, flyspell now uses a cache in order
|
|
81 ;; to improve efficiency and make uses of a pre-command-hook
|
|
82 ;; in order to check a word when living it.
|
|
83 ;; - Remaned flyspell-ignore-command into
|
|
84 ;; flyspell-delay-command.
|
|
85 ;; - Add the flyspell-issue-welcome (as suggested by Joshua
|
|
86 ;; Guttman).
|
|
87 ;; - Ispell process are now killed when the buffer they are
|
|
88 ;; running in is deleted (thanks to Jeff Miller and Roland
|
|
89 ;; Rosenfled).
|
|
90 ;; - When used on a B&W terminal flyspell used boldness instead
|
|
91 ;; of color for incorrect words.
|
|
92 ;; Release 1.2:
|
|
93 ;; - Breaks (space or newline) inside incorrect words are now
|
|
94 ;; better handled.
|
|
95 ;; - Flyspell sorts the proposed replacement words (thanks to
|
|
96 ;; Carsten Dominik). See new variable
|
|
97 ;; `flyspell-sort-corrections'.
|
|
98 ;; - The mouse binding to correct mispelled word is now mouse-2
|
|
99 ;; on an highlighted region. This enhancement (as well as a
|
|
100 ;; lot of code cleaning) has been acheived by Carsten Dominik.
|
|
101 ;; - flyspell-mode arg is now optional.
|
|
102 ;; - flyspell bindings are better displayed.
|
|
103 ;; - flyspell nows is able to handle concurent and different
|
|
104 ;; dictionaries (that each buffer running flyspell uses its
|
|
105 ;; own (buffer local) Ispell process).
|
|
106 ;; - default value for flyspell-highlight-property has been
|
|
107 ;; turned to t.
|
|
108 ;; - flyspell popup menus now support session and buffer
|
|
109 ;; dictionaries.
|
|
110 ;; - corrected words are now correctly unhighlighted (no
|
|
111 ;; highlighted characters left).
|
|
112 ;; Note: I think emacs-19.34 has a bug on the overlay event
|
|
113 ;; handling. When an overlay (or a text property) has uses a
|
|
114 ;; local-map, if this map does not include a key binding,
|
|
115 ;; instead of looking at the enclosing local-map emacs-19.34
|
|
116 ;; uses the global-map. I have not tested this with emacs-20.
|
|
117 ;; I have checked with Xemacs that does contain this error.
|
|
118 ;; Release 1.1:
|
|
119 ;; - Add an automatic replacement for incorrect word.
|
|
120 ;; Release 1.0:
|
|
121 ;; - Add popup menu for fast correction.
|
|
122 ;; Release 0.9:
|
|
123 ;; - Add an Ispell bug workaround. Now, in french mode, word
|
|
124 ;; starting by the '-' character does not, any longer, make
|
|
125 ;; Ispell to fall in infinite loops.
|
|
126 ;; Release 0.8:
|
|
127 ;; - Better Xemacs support
|
|
128 ;; Release 0.7:
|
|
129 ;; - Rather than hard-coding the ignored commend I now uses a
|
|
130 ;; property field to check if a command is ignored. The
|
|
131 ;; advantage is that user may now add its own ignored
|
|
132 ;; commands.
|
|
133 ;; Release 0.6:
|
|
134 ;; - Fix flyspell mode name (in modeline bar) bug.
|
|
135 ;; - Fix the bug on flyspell quitting. Overlays are now really
|
|
136 ;; removed.
|
|
137 ;; Release 0.5:
|
|
138 ;; - Persistent hilightings.
|
|
139 ;; - Refresh of the modeline on flyspell ending
|
|
140 ;; - Do not hilight text with properties (e.g. font lock text)
|
|
141
|
|
142 ;;; Code:
|
|
143 (require 'font-lock)
|
|
144 (require 'ispell)
|
|
145
|
|
146 ;*---------------------------------------------------------------------*/
|
|
147 ;* defcustom stuff. This ensure that we have the correct custom */
|
|
148 ;* library. */
|
|
149 ;*---------------------------------------------------------------------*/
|
|
150 (eval-and-compile
|
|
151 (condition-case () (require 'custom) (error nil))
|
|
152 (if (and (featurep 'custom) (fboundp 'custom-declare-variable))
|
|
153 ;; We have got what we need
|
|
154 (if (not (string-match "XEmacs" emacs-version))
|
|
155 ;; suppress warnings
|
|
156 (progn
|
|
157 ;; This is part of bytecomp.el in 19.35:
|
|
158 (put 'custom-declare-variable 'byte-hunk-handler
|
|
159 'byte-compile-file-form-custom-declare-variable)
|
|
160 (defun byte-compile-file-form-custom-declare-variable (form)
|
|
161 (if (memq 'free-vars byte-compile-warnings)
|
|
162 (setq byte-compile-bound-variables
|
|
163 (cons (nth 1 (nth 1 form))
|
|
164 byte-compile-bound-variables)))
|
|
165 form)))
|
|
166 ;; We have the old custom-library, hack around it!
|
|
167 (defmacro defgroup (&rest args) nil)
|
|
168 (defmacro defcustom (var value doc &rest args)
|
|
169 (` (defvar (, var) (, value) (, doc))))))
|
|
170
|
|
171 (defgroup flyspell nil
|
|
172 "Spellchecking on the fly."
|
|
173 :tag "FlySpell"
|
|
174 :prefix "flyspell-"
|
|
175 :group 'processes)
|
|
176
|
|
177 ;*---------------------------------------------------------------------*/
|
|
178 ;* User variables ... */
|
|
179 ;*---------------------------------------------------------------------*/
|
|
180 (defcustom flyspell-highlight-flag t
|
|
181 "*Non-nil means use highlight, nil means use mini-buffer messages."
|
|
182 :group 'flyspell
|
|
183 :type 'boolean)
|
|
184
|
|
185 (defcustom flyspell-doublon-as-error-flag t
|
|
186 "*Non-nil means consider doublon as misspelling."
|
|
187 :group 'flyspell
|
|
188 :type 'boolean)
|
|
189
|
|
190 (defcustom flyspell-sort-corrections t
|
|
191 "*Non-nil means, sort the corrections alphabetically before popping them."
|
|
192 :group 'flyspell
|
|
193 :type 'boolean)
|
|
194
|
|
195 (defcustom flyspell-incorrect-color "OrangeRed"
|
|
196 "*The color used for highlighting incorrect words."
|
|
197 :group 'flyspell
|
|
198 :type 'string)
|
|
199
|
|
200 (defcustom flyspell-duplicate-color "Gold3"
|
|
201 "*The color used for highlighting incorrect words but appearing at least twice."
|
|
202 :group 'flyspell
|
|
203 :type 'string)
|
|
204
|
|
205 (defcustom flyspell-underline-p t
|
|
206 "*Non-nil means, incorrect words are underlined."
|
|
207 :group 'flyspell
|
|
208 :type 'boolean)
|
|
209
|
|
210 (defcustom flyspell-auto-correct-binding
|
|
211 "\M-\t"
|
|
212 "*Non-nil means that its value (a binding) will bound to the flyspell
|
|
213 auto-correct."
|
|
214 :group 'flyspell
|
|
215 :type '(choice (const nil string)))
|
|
216
|
|
217 (defcustom flyspell-command-hook t
|
|
218 "*Non-nil means that `post-command-hook' is used to check
|
|
219 already typed words."
|
|
220 :group 'flyspell
|
|
221 :type 'boolean)
|
|
222
|
|
223 (defcustom flyspell-duplicate-distance -1
|
|
224 "*The distance from duplication.
|
|
225 -1 means no limit.
|
|
226 0 means no window."
|
|
227 :group 'flyspell
|
|
228 :type 'number)
|
|
229
|
|
230 (defcustom flyspell-delay 3
|
|
231 "*The number of second before checking words on post-command-hook if
|
|
232 the current command is a delay command."
|
|
233 :group 'flyspell
|
|
234 :type 'number)
|
|
235
|
|
236 (defcustom flyspell-persistent-highlight t
|
|
237 "*T means that hilighted words are not removed until the word are corrected."
|
|
238 :group 'flyspell
|
|
239 :type 'boolean)
|
|
240
|
|
241 (defcustom flyspell-highlight-properties t
|
|
242 "*T means highlight incorrect words even if a property exists for this word."
|
|
243 :group 'flyspell
|
|
244 :type 'boolean)
|
|
245
|
|
246 (defcustom flyspell-default-delayed-commands
|
|
247 '(self-insert-command
|
|
248 delete-backward-char
|
|
249 delete-char)
|
|
250 "The list of always delayed command (that is flyspell is not activated
|
|
251 after any of these commands."
|
|
252 :group 'flyspell
|
|
253 :type '(repeat (symbol)))
|
|
254
|
|
255 (defcustom flyspell-delayed-commands
|
|
256 nil
|
|
257 "*If non nil, this variable must hold a list a symbol. Each symbol is
|
|
258 the name of an delayed command (that is a command that does not activate
|
|
259 flyspell checking."
|
|
260 :group 'flyspell
|
|
261 :type '(repeat (symbol)))
|
|
262
|
|
263 (defcustom flyspell-issue-welcome-flag t
|
|
264 "*Non-nil means that flyspell issues a welcome message when started."
|
|
265 :group 'flyspell
|
|
266 :type 'boolean)
|
|
267
|
|
268 (defcustom flyspell-consider-dash-as-word-delimiter-flag nil
|
|
269 "*Non-nil means that the `-' char is considered as a word delimiter."
|
|
270 :group 'flyspell
|
|
271 :type 'boolean)
|
|
272
|
|
273 (defcustom flyspell-incorrect-hook nil
|
|
274 "*Non-nil means a list of hooks to be executed when incorrect
|
|
275 words are encountered. Each hook is a function of two arguments that are
|
|
276 location of the beginning and the end of the incorrect region."
|
|
277 :group 'flyspell)
|
|
278
|
|
279 (defcustom flyspell-multi-language-p t
|
|
280 "*Non-nil means that flyspell could be use with several buffers checking
|
|
281 several languages. Non-nil means that a new ispell process will be spawned
|
|
282 per buffer. If nil, only one unique ispell process will be running."
|
|
283 :group 'flyspell
|
|
284 :type 'boolean)
|
|
285
|
|
286 ;*---------------------------------------------------------------------*/
|
|
287 ;* Mode specific options */
|
|
288 ;* ------------------------------------------------------------- */
|
|
289 ;* Mode specific options enable users to disable flyspell on */
|
|
290 ;* certain word depending of the emacs mode. For instance, when */
|
|
291 ;* using flyspell with mail-mode add the following expression */
|
|
292 ;* in your .emacs file: */
|
|
293 ;* (add-hook 'mail-mode */
|
|
294 ;* '(lambda () (setq flyspell-generic-check-word-p */
|
|
295 ;* 'mail-mode-flyspell-verify))) */
|
|
296 ;*---------------------------------------------------------------------*/
|
|
297 (defvar flyspell-generic-check-word-p nil
|
|
298 "Function providing per-mode customization over which words are flyspelled.
|
|
299 Returns t to continue checking, nil otherwise.")
|
|
300 (make-variable-buffer-local 'flyspell-generic-check-word-p)
|
|
301
|
|
302 (defun mail-mode-flyspell-verify ()
|
|
303 "Return t if we want flyspell to check the word under point."
|
|
304 (save-excursion
|
|
305 (not (or (re-search-forward mail-header-separator nil t)
|
|
306 (re-search-backward message-signature-separator nil t)
|
|
307 (progn
|
|
308 (beginning-of-line)
|
|
309 (looking-at "[>}|]"))))))
|
|
310
|
|
311 (defun texinfo-mode-flyspell-verify ()
|
|
312 "Return t if we want flyspell to check the word under point."
|
|
313 (save-excursion
|
|
314 (forward-word -1)
|
|
315 (not (looking-at "@"))))
|
|
316
|
|
317 ;*---------------------------------------------------------------------*/
|
|
318 ;* Overlay compatibility */
|
|
319 ;*---------------------------------------------------------------------*/
|
|
320 (autoload 'make-overlay "overlay" "" t)
|
|
321 (autoload 'move-overlay "overlay" "" t)
|
|
322 (autoload 'overlayp "overlay" "" t)
|
|
323 (autoload 'overlay-properties "overlay" "" t)
|
|
324 (autoload 'overlays-in "overlay" "" t)
|
|
325 (autoload 'delete-overlay "overlay" "" t)
|
|
326 (autoload 'overlays-at "overlay" "" t)
|
|
327 (autoload 'overlay-put "overlay" "" t)
|
|
328 (autoload 'overlay-get "overlay" "" t)
|
|
329
|
|
330 (defun flyspell-font-lock-make-face (l)
|
|
331 "Because emacs and xemacs does not behave the same I uses my owe
|
|
332 font-lock-make-face function. This function is similar to the gnu-emacs
|
|
333 font-lock-make-face function."
|
|
334 (let ((fname (car l))
|
|
335 (color (car (cdr l)))
|
|
336 (italic (car (cdr (cdr l))))
|
|
337 (bold (car (cdr (cdr (cdr l)))))
|
|
338 (underline (car (cdr (cdr (cdr (cdr l)))))))
|
|
339 (let ((face (copy-face 'default fname)))
|
|
340 (if color
|
|
341 (set-face-foreground face color))
|
|
342 (if (and italic bold)
|
|
343 (condition-case nil
|
|
344 (make-face-bold-italic face)
|
|
345 (error nil))
|
|
346 (progn
|
|
347 (if italic
|
|
348 (condition-case nil
|
|
349 (make-face-italic face)
|
|
350 (error nil)))
|
|
351 (if bold
|
|
352 (condition-case nil
|
|
353 (make-face-bold face)
|
|
354 (error nil)))))
|
|
355 (if underline
|
|
356 (condition-case nil
|
|
357 (set-face-underline-p face t)
|
|
358 (error nil)))
|
|
359 (if (not (x-display-color-p))
|
|
360 (condition-case nil
|
|
361 (make-face-bold face)
|
|
362 (error nil)))
|
|
363 face)))
|
|
364
|
|
365 ;*---------------------------------------------------------------------*/
|
|
366 ;* Which emacs are we currently running */
|
|
367 ;*---------------------------------------------------------------------*/
|
|
368 (defvar flyspell-emacs
|
|
369 (cond
|
|
370 ((string-match "XEmacs" emacs-version)
|
|
371 'xemacs)
|
|
372 (t
|
|
373 'emacs))
|
|
374 "The Emacs we are currently running.")
|
|
375
|
|
376 ;*---------------------------------------------------------------------*/
|
|
377 ;* cl compatibility */
|
|
378 ;*---------------------------------------------------------------------*/
|
|
379 (defmacro push (x place)
|
|
380 "(push X PLACE): insert X at the head of the list stored in PLACE.
|
|
381 Analogous to (setf PLACE (cons X PLACE)), though more careful about
|
|
382 evaluating each argument only once and in the right order. PLACE has
|
|
383 to be a symbol."
|
|
384 (list 'setq place (list 'cons x place)))
|
|
385
|
|
386 ;*---------------------------------------------------------------------*/
|
|
387 ;* The minor mode declaration. */
|
|
388 ;*---------------------------------------------------------------------*/
|
|
389 (defvar flyspell-mode nil)
|
|
390 (make-variable-buffer-local 'flyspell-mode)
|
|
391
|
|
392 (defvar flyspell-mode-map (make-sparse-keymap))
|
|
393 (defvar flyspell-mouse-map (make-sparse-keymap))
|
|
394
|
|
395 (or (assoc 'flyspell-mode minor-mode-alist)
|
|
396 (push '(flyspell-mode " Fly") minor-mode-alist))
|
|
397
|
|
398 (or (assoc 'flyspell-mode minor-mode-map-alist)
|
|
399 (push (cons 'flyspell-mode flyspell-mode-map) minor-mode-map-alist))
|
|
400
|
|
401 (if flyspell-auto-correct-binding
|
|
402 (define-key flyspell-mode-map flyspell-auto-correct-binding
|
|
403 (function flyspell-auto-correct-word)))
|
|
404 ;; mouse bindings
|
|
405 (if (eq flyspell-emacs 'xemacs)
|
|
406 (define-key flyspell-mouse-map [(button2)]
|
|
407 (function flyspell-correct-word/mouse-keymap))
|
|
408 (define-key flyspell-mode-map [(mouse-2)]
|
|
409 (function flyspell-correct-word/local-keymap)))
|
|
410
|
|
411 ;; the name of the overlay property that defines the keymap
|
|
412 (defvar flyspell-overlay-keymap-property-name
|
|
413 (if (string-match "19.*XEmacs" emacs-version)
|
|
414 'keymap
|
|
415 'local-map))
|
|
416
|
|
417 ;*---------------------------------------------------------------------*/
|
|
418 ;* Highlighting */
|
|
419 ;*---------------------------------------------------------------------*/
|
|
420 (flyspell-font-lock-make-face (list 'flyspell-incorrect-face
|
|
421 flyspell-incorrect-color
|
|
422 nil
|
|
423 t
|
|
424 flyspell-underline-p))
|
|
425 (flyspell-font-lock-make-face (list 'flyspell-duplicate-face
|
|
426 flyspell-duplicate-color
|
|
427 nil
|
|
428 t
|
|
429 flyspell-underline-p))
|
|
430
|
|
431 (defvar flyspell-overlay nil)
|
|
432
|
|
433 ;*---------------------------------------------------------------------*/
|
|
434 ;* flyspell-mode ... */
|
|
435 ;*---------------------------------------------------------------------*/
|
|
436 ;;;###autoload
|
|
437 (defun flyspell-mode (&optional arg)
|
|
438 "Minor mode performing on-the-fly spelling checking.
|
|
439 Ispell is automatically spawned on background for each entered words.
|
|
440 The default flyspells behavior is to highlight incorrect words.
|
|
441 With prefix ARG, turn Flyspell minor mode on iff ARG is positive.
|
|
442
|
|
443 Bindings:
|
|
444 \\[ispell-word]: correct words (using Ispell).
|
|
445 \\[flyspell-auto-correct-word]: automatically correct word.
|
|
446 \\[flyspell-correct-word] (or mouse-2): popup correct words.
|
|
447
|
|
448 Hooks:
|
|
449 flyspell-mode-hook is runner after flyspell is entered.
|
|
450
|
|
451 Remark:
|
|
452 `flyspell-mode' uses `ispell-mode'. Thus all Ispell options are
|
|
453 valid. For instance, a personal dictionary can be used by
|
|
454 invoking `ispell-change-dictionary'.
|
|
455
|
|
456 Consider using the `ispell-parser' to check your text. For instance
|
|
457 consider adding:
|
|
458 (add-hook 'tex-mode-hook (function (lambda () (setq ispell-parser 'tex))))
|
|
459 in your .emacs file.
|
|
460
|
|
461 flyspell-region checks all words inside a region.
|
|
462
|
|
463 flyspell-buffer checks the whole buffer."
|
|
464 (interactive "P")
|
|
465 ;; we set the mode on or off
|
|
466 (setq flyspell-mode (not (or (and (null arg) flyspell-mode)
|
|
467 (<= (prefix-numeric-value arg) 0))))
|
|
468 (if flyspell-mode
|
|
469 (flyspell-mode-on)
|
|
470 (flyspell-mode-off))
|
|
471 ;; we force the modeline re-printing
|
|
472 (set-buffer-modified-p (buffer-modified-p)))
|
|
473
|
|
474 ;*---------------------------------------------------------------------*/
|
|
475 ;* flyspell-mode-on ... */
|
|
476 ;*---------------------------------------------------------------------*/
|
|
477 (defun flyspell-mode-on ()
|
|
478 "Turn flyspell mode on. Do not use, use `flyspell-mode' instead."
|
|
479 (message "flyspell on: %S" (current-buffer))
|
|
480 (setq ispell-highlight-face 'flyspell-incorrect-face)
|
|
481 ;; ispell initialization
|
|
482 (if flyspell-multi-language-p
|
|
483 (progn
|
|
484 (make-variable-buffer-local 'ispell-dictionary)
|
|
485 (make-variable-buffer-local 'ispell-process)
|
|
486 (make-variable-buffer-local 'ispell-filter)
|
|
487 (make-variable-buffer-local 'ispell-filter-continue)
|
|
488 (make-variable-buffer-local 'ispell-process-directory)
|
|
489 (make-variable-buffer-local 'ispell-parser)))
|
|
490 ;; we initialize delayed commands symbol
|
|
491 (flyspell-delay-commands)
|
|
492 ;; we bound flyspell action to post-command hook
|
|
493 (if flyspell-command-hook
|
|
494 (progn
|
|
495 (make-local-hook 'post-command-hook)
|
|
496 (add-hook 'post-command-hook
|
|
497 (function flyspell-post-command-hook)
|
|
498 t
|
|
499 t)))
|
|
500 ;; we bound flyspell action to pre-command hook
|
|
501 (if flyspell-command-hook
|
|
502 (progn
|
|
503 (make-local-hook 'pre-command-hook)
|
|
504 (add-hook 'pre-command-hook
|
|
505 (function flyspell-pre-command-hook)
|
|
506 t
|
|
507 t)))
|
|
508 ;; the welcome message
|
|
509 (if flyspell-issue-welcome-flag
|
|
510 (message
|
|
511 (if flyspell-auto-correct-binding
|
|
512 (format "Welcome to flyspell. Use %S or mouse-2 to correct words."
|
|
513 (key-description flyspell-auto-correct-binding))
|
|
514 "Welcome to flyspell. Use mouse-2 to correct words.")))
|
|
515 ;; we have to kill the flyspell process when the buffer is deleted.
|
|
516 ;; (thanks to Jeff Miller and Roland Rosenfeld who sent me this
|
|
517 ;; improvement).
|
|
518 (add-hook 'kill-buffer-hook
|
|
519 '(lambda ()
|
|
520 (if flyspell-mode
|
|
521 (flyspell-mode-off))))
|
|
522 ;; we end with the flyspell hooks
|
|
523 (run-hooks 'flyspell-mode-hook))
|
|
524
|
|
525 ;*---------------------------------------------------------------------*/
|
|
526 ;* flyspell-delay-commands ... */
|
|
527 ;*---------------------------------------------------------------------*/
|
|
528 (defun flyspell-delay-commands ()
|
|
529 "Install the delayed command."
|
|
530 (mapcar 'flyspell-delay-command flyspell-default-delayed-commands)
|
|
531 (mapcar 'flyspell-delay-command flyspell-delayed-commands))
|
|
532
|
|
533 ;*---------------------------------------------------------------------*/
|
|
534 ;* flyspell-delay-command ... */
|
|
535 ;*---------------------------------------------------------------------*/
|
|
536 (defun flyspell-delay-command (command)
|
|
537 "Set COMMAND to be delayed.
|
|
538 When flyspell `post-command-hook' is invoked because a delayed command
|
|
539 as been used the current word is not immediatly checked.
|
|
540 It will be checked only after flyspell-delay second."
|
|
541 (interactive "Scommand: ")
|
|
542 (put command 'flyspell-delayed t))
|
|
543
|
|
544 ;*---------------------------------------------------------------------*/
|
|
545 ;* flyspell-ignore-commands ... */
|
|
546 ;*---------------------------------------------------------------------*/
|
|
547 (defun flyspell-ignore-commands ()
|
|
548 "This is an obsolete function, use flyspell-delays command instead."
|
|
549 (flyspell-delay-commands))
|
|
550
|
|
551 ;*---------------------------------------------------------------------*/
|
|
552 ;* flyspell-ignore-command ... */
|
|
553 ;*---------------------------------------------------------------------*/
|
|
554 (defun flyspell-ignore-command (command)
|
|
555 "This is an obsolete function, use flyspell-delay command instead.
|
|
556 COMMAND is the name of the command to be delayed."
|
|
557 (flyspell-delay-command command))
|
|
558
|
|
559 (make-obsolete 'flyspell-ignore-commands 'flyspell-delay-commands)
|
|
560 (make-obsolete 'flyspell-ignore-command 'flyspell-delay-command)
|
|
561
|
|
562 ;*---------------------------------------------------------------------*/
|
|
563 ;* flyspell-word-cache ... */
|
|
564 ;*---------------------------------------------------------------------*/
|
|
565 (defvar flyspell-word-cache-start nil)
|
|
566 (defvar flyspell-word-cache-end nil)
|
|
567 (defvar flyspell-word-cache-word nil)
|
|
568 (make-variable-buffer-local 'flyspell-word-cache-start)
|
|
569 (make-variable-buffer-local 'flyspell-word-cache-end)
|
|
570 (make-variable-buffer-local 'flyspell-word-cache-word)
|
|
571
|
|
572 ;*---------------------------------------------------------------------*/
|
|
573 ;* The flyspell pre-hook, store the current position. In the */
|
|
574 ;* post command hook, we will check, if the word at this position */
|
|
575 ;* has to be spell checked. */
|
|
576 ;*---------------------------------------------------------------------*/
|
|
577 (defvar flyspell-pre-buffer nil)
|
|
578 (defvar flyspell-pre-point nil)
|
|
579
|
|
580 ;*---------------------------------------------------------------------*/
|
|
581 ;* flyspell-pre-command-hook ... */
|
|
582 ;*---------------------------------------------------------------------*/
|
|
583 (defun flyspell-pre-command-hook ()
|
|
584 "This function is internally used by Flyspell to get a cursor location
|
|
585 before a user command."
|
|
586 (interactive)
|
|
587 (setq flyspell-pre-buffer (current-buffer))
|
|
588 (setq flyspell-pre-point (point)))
|
|
589
|
|
590 ;*---------------------------------------------------------------------*/
|
|
591 ;* flyspell-mode-off ... */
|
|
592 ;*---------------------------------------------------------------------*/
|
|
593 (defun flyspell-mode-off ()
|
|
594 "Turn flyspell mode off. Do not use. Use `flyspell-mode' instead."
|
|
595 ;; the bye-bye message
|
|
596 (message "Quiting Flyspell...%S" (current-buffer))
|
|
597 ;; we stop the running ispell
|
|
598 (ispell-kill-ispell t)
|
|
599 ;; we remove the hooks
|
|
600 (if flyspell-command-hook
|
|
601 (progn
|
|
602 (remove-hook 'post-command-hook
|
|
603 (function flyspell-post-command-hook)
|
|
604 t)
|
|
605 (remove-hook 'pre-command-hook
|
|
606 (function flyspell-pre-command-hook)
|
|
607 t)))
|
|
608 ;; we remove all the flyspell hilightings
|
|
609 (flyspell-delete-all-overlays)
|
|
610 ;; we have to erase pre cache variables
|
|
611 (setq flyspell-pre-buffer nil)
|
|
612 (setq flyspell-pre-point nil)
|
|
613 ;; we mark the mode as killed
|
|
614 (setq flyspell-mode nil))
|
|
615
|
|
616 ;*---------------------------------------------------------------------*/
|
|
617 ;* flyspell-check-word-p ... */
|
|
618 ;*---------------------------------------------------------------------*/
|
|
619 (defun flyspell-check-word-p ()
|
|
620 "This function returns t when the word at `point' has to be
|
|
621 checked. The answer depends of several criteria. Mostly we
|
|
622 check word delimiters."
|
|
623 (cond
|
|
624 ((<= (- (point-max) 1) (point-min))
|
|
625 ;; the buffer is not filled enough
|
|
626 nil)
|
|
627 ((not (and (symbolp this-command) (get this-command 'flyspell-delayed)))
|
|
628 ;; the current command is not delayed, that
|
|
629 ;; is that we must check the word now
|
|
630 t)
|
|
631 ((and (> (point) (point-min))
|
|
632 (save-excursion
|
|
633 (backward-char 1)
|
|
634 (and (looking-at (flyspell-get-not-casechars))
|
|
635 (or flyspell-consider-dash-as-word-delimiter-flag
|
|
636 (not (looking-at "\\-"))))))
|
|
637 ;; yes because we have reached or typed a word delimiter
|
|
638 t)
|
|
639 ((not (integerp flyspell-delay))
|
|
640 ;; yes because the user had settup a non delay configuration
|
|
641 t)
|
|
642 (t
|
|
643 (if (fboundp 'about-xemacs)
|
|
644 (sit-for flyspell-delay nil)
|
|
645 (sit-for flyspell-delay 0 nil)))))
|
|
646
|
|
647 ;*---------------------------------------------------------------------*/
|
|
648 ;* flyspell-check-pre-word-p ... */
|
|
649 ;*---------------------------------------------------------------------*/
|
|
650 (defun flyspell-check-pre-word-p ()
|
|
651 "When to we have to check the word that was at point before
|
|
652 the current command?"
|
|
653 (cond
|
|
654 ((or (not (numberp flyspell-pre-point))
|
|
655 (not (bufferp flyspell-pre-buffer))
|
|
656 (not (buffer-live-p flyspell-pre-buffer)))
|
|
657 nil)
|
|
658 ((or (= flyspell-pre-point (- (point) 1))
|
|
659 (= flyspell-pre-point (point))
|
|
660 (= flyspell-pre-point (+ (point) 1)))
|
|
661 nil)
|
|
662 ((not (eq (current-buffer) flyspell-pre-buffer))
|
|
663 t)
|
|
664 ((not (and (numberp flyspell-word-cache-start)
|
|
665 (numberp flyspell-word-cache-end)))
|
|
666 t)
|
|
667 (t
|
|
668 (or (< flyspell-pre-point flyspell-word-cache-start)
|
|
669 (> flyspell-pre-point flyspell-word-cache-end)))))
|
|
670
|
|
671 ;*---------------------------------------------------------------------*/
|
|
672 ;* flyspell-post-command-hook ... */
|
|
673 ;*---------------------------------------------------------------------*/
|
|
674 (defun flyspell-post-command-hook ()
|
|
675 "The `post-command-hook' used by flyspell to check a word in-the-fly."
|
|
676 (interactive)
|
|
677 (if (flyspell-check-word-p)
|
|
678 (flyspell-word))
|
|
679 (if (flyspell-check-pre-word-p)
|
|
680 (save-excursion
|
|
681 (set-buffer flyspell-pre-buffer)
|
|
682 (save-excursion
|
|
683 (goto-char flyspell-pre-point)
|
|
684 (flyspell-word)))))
|
|
685
|
|
686 ;*---------------------------------------------------------------------*/
|
|
687 ;* flyspell-word ... */
|
|
688 ;*---------------------------------------------------------------------*/
|
|
689 (defun flyspell-word (&optional following)
|
|
690 "Spell check a word."
|
|
691 (interactive (list current-prefix-arg))
|
|
692 (if (interactive-p)
|
|
693 (setq following ispell-following-word))
|
|
694 (save-excursion
|
|
695 (ispell-accept-buffer-local-defs) ; use the correct dictionary
|
|
696 (let ((cursor-location (point)) ; retain cursor location
|
|
697 (word (flyspell-get-word following))
|
|
698 start end poss)
|
|
699 (if (or (eq word nil)
|
|
700 (and (fboundp flyspell-generic-check-word-p)
|
|
701 (not (funcall flyspell-generic-check-word-p))))
|
|
702 t
|
|
703 (progn
|
|
704 ;; destructure return word info list.
|
|
705 (setq start (car (cdr word))
|
|
706 end (car (cdr (cdr word)))
|
|
707 word (car word))
|
|
708 ;; before checking in the directory, we check for doublons.
|
|
709 (cond
|
|
710 ((and flyspell-doublon-as-error-flag
|
|
711 (save-excursion
|
|
712 (goto-char start)
|
|
713 (word-search-backward word
|
|
714 (- start
|
|
715 (+ 1 (- end start)))
|
|
716 t)))
|
|
717 ;; yes, this is a doublon
|
|
718 (flyspell-highlight-incorrect-region start end))
|
|
719 ((and (eq flyspell-word-cache-start start)
|
|
720 (eq flyspell-word-cache-end end)
|
|
721 (string-equal flyspell-word-cache-word word))
|
|
722 ;; this word had been already checked, we skip
|
|
723 nil)
|
|
724 ((and (eq ispell-parser 'tex)
|
|
725 (flyspell-tex-command-p word))
|
|
726 ;; this is a correct word (because a tex command)
|
|
727 (flyspell-unhighlight-at start)
|
|
728 (if (> end start)
|
|
729 (flyspell-unhighlight-at (- end 1)))
|
|
730 t)
|
|
731 (t
|
|
732 ;; we setup the cache
|
|
733 (setq flyspell-word-cache-start start)
|
|
734 (setq flyspell-word-cache-end end)
|
|
735 (setq flyspell-word-cache-word word)
|
|
736 ;; now check spelling of word.
|
|
737 (process-send-string ispell-process "%\n")
|
|
738 ;; put in verbose mode
|
|
739 (process-send-string ispell-process
|
|
740 (concat "^" word "\n"))
|
|
741 ;; wait until ispell has processed word
|
|
742 (while (progn
|
|
743 (accept-process-output ispell-process)
|
|
744 (not (string= "" (car ispell-filter)))))
|
|
745 ;; (process-send-string ispell-process "!\n")
|
|
746 ;; back to terse mode.
|
|
747 (setq ispell-filter (cdr ispell-filter))
|
|
748 (if (listp ispell-filter)
|
|
749 (setq poss (ispell-parse-output (car ispell-filter))))
|
|
750 (cond ((eq poss t)
|
|
751 ;; correct
|
|
752 (flyspell-unhighlight-at start)
|
|
753 (if (> end start)
|
|
754 (flyspell-unhighlight-at (- end 1)))
|
|
755 t)
|
|
756 ((and (stringp poss) flyspell-highlight-flag)
|
|
757 ;; correct
|
|
758 (flyspell-unhighlight-at start)
|
|
759 (if (> end start)
|
|
760 (flyspell-unhighlight-at (- end 1)))
|
|
761 t)
|
|
762 ((null poss)
|
|
763 (flyspell-unhighlight-at start)
|
|
764 (if (> end start)
|
|
765 (flyspell-unhighlight-at (- end 1)))
|
|
766 (message "Error in ispell process"))
|
|
767 ((or (and (< flyspell-duplicate-distance 0)
|
|
768 (or (save-excursion
|
|
769 (goto-char start)
|
|
770 (word-search-backward word
|
|
771 (point-min)
|
|
772 t))
|
|
773 (save-excursion
|
|
774 (goto-char end)
|
|
775 (word-search-forward word
|
|
776 (point-max)
|
|
777 t))))
|
|
778 (and (> flyspell-duplicate-distance 0)
|
|
779 (or (save-excursion
|
|
780 (goto-char start)
|
|
781 (word-search-backward
|
|
782 word
|
|
783 (- start
|
|
784 flyspell-duplicate-distance)
|
|
785 t))
|
|
786 (save-excursion
|
|
787 (goto-char end)
|
|
788 (word-search-forward
|
|
789 word
|
|
790 (+ end
|
|
791 flyspell-duplicate-distance)
|
|
792 t)))))
|
|
793 (if flyspell-highlight-flag
|
|
794 (flyspell-highlight-duplicate-region start end)
|
|
795 (message (format "misspelling duplicate `%s'"
|
|
796 word))))
|
|
797 (t
|
|
798 ;; incorrect highlight the location
|
|
799 (if flyspell-highlight-flag
|
|
800 (flyspell-highlight-incorrect-region start end)
|
|
801 (message (format "mispelling `%s'" word)))))
|
|
802 (goto-char cursor-location) ; return to original location
|
|
803 (if ispell-quit (setq ispell-quit nil)))))))))
|
|
804
|
|
805 ;*---------------------------------------------------------------------*/
|
|
806 ;* flyspell-tex-command-p ... */
|
|
807 ;*---------------------------------------------------------------------*/
|
|
808 (defun flyspell-tex-command-p (word)
|
|
809 "Is a word a TeX command?"
|
|
810 (eq (aref word 0) ?\\))
|
|
811
|
|
812 ;*---------------------------------------------------------------------*/
|
|
813 ;* flyspell-casechars-cache ... */
|
|
814 ;*---------------------------------------------------------------------*/
|
|
815 (defvar flyspell-casechars-cache nil)
|
|
816 (defvar flyspell-ispell-casechars-cache nil)
|
|
817 (make-variable-buffer-local 'flyspell-casechars-cache)
|
|
818 (make-variable-buffer-local 'flyspell-ispell-casechars-cache)
|
|
819
|
|
820 ;*---------------------------------------------------------------------*/
|
|
821 ;* flyspell-get-casechars ... */
|
|
822 ;*---------------------------------------------------------------------*/
|
|
823 (defun flyspell-get-casechars ()
|
|
824 "This function builds a string that is the regexp of word chars.
|
|
825 In order
|
|
826 to avoid one useless string construction, this function changes the last
|
|
827 char of the ispell-casechars string."
|
|
828 (let ((ispell-casechars (ispell-get-casechars)))
|
|
829 (cond
|
|
830 ((eq ispell-casechars flyspell-ispell-casechars-cache)
|
|
831 flyspell-casechars-cache)
|
|
832 ((not (eq ispell-parser 'tex))
|
|
833 (setq flyspell-ispell-casechars-cache ispell-casechars)
|
|
834 (setq flyspell-casechars-cache
|
|
835 (concat (substring ispell-casechars
|
|
836 0
|
|
837 (- (length ispell-casechars) 1))
|
|
838 "{}]"))
|
|
839 flyspell-casechars-cache)
|
|
840 (t
|
|
841 (setq flyspell-ispell-casechars-cache ispell-casechars)
|
|
842 (setq flyspell-casechars-cache ispell-casechars)
|
|
843 flyspell-casechars-cache))))
|
|
844
|
|
845 ;*---------------------------------------------------------------------*/
|
|
846 ;* flyspell-get-not-casechars-cache ... */
|
|
847 ;*---------------------------------------------------------------------*/
|
|
848 (defvar flyspell-not-casechars-cache nil)
|
|
849 (defvar flyspell-ispell-not-casechars-cache nil)
|
|
850 (make-variable-buffer-local 'flyspell-not-casechars-cache)
|
|
851 (make-variable-buffer-local 'flyspell-ispell-not-casechars-cache)
|
|
852
|
|
853 ;*---------------------------------------------------------------------*/
|
|
854 ;* flyspell-get-not-casechars ... */
|
|
855 ;*---------------------------------------------------------------------*/
|
|
856 (defun flyspell-get-not-casechars ()
|
|
857 "This function builds a string that is the regexp of non-word chars."
|
|
858 (let ((ispell-not-casechars (ispell-get-not-casechars)))
|
|
859 (cond
|
|
860 ((eq ispell-not-casechars flyspell-ispell-not-casechars-cache)
|
|
861 flyspell-not-casechars-cache)
|
|
862 ((not (eq ispell-parser 'tex))
|
|
863 (setq flyspell-ispell-not-casechars-cache ispell-not-casechars)
|
|
864 (setq flyspell-not-casechars-cache
|
|
865 (concat (substring ispell-not-casechars
|
|
866 0
|
|
867 (- (length ispell-not-casechars) 1))
|
|
868 "{}]"))
|
|
869 flyspell-not-casechars-cache)
|
|
870 (t
|
|
871 (setq flyspell-ispell-not-casechars-cache ispell-not-casechars)
|
|
872 (setq flyspell-not-casechars-cache ispell-not-casechars)
|
|
873 flyspell-not-casechars-cache))))
|
|
874
|
|
875 ;*---------------------------------------------------------------------*/
|
|
876 ;* flyspell-get-word ... */
|
|
877 ;*---------------------------------------------------------------------*/
|
|
878 (defun flyspell-get-word (following)
|
|
879 "Return the word for spell-checking according to Ispell syntax.
|
|
880 If optional argument FOLLOWING is non-nil or if `ispell-following-word'
|
|
881 is non-nil when called interactively, then the following word
|
|
882 \(rather than preceding\) is checked when the cursor is not over a word.
|
|
883 Optional second argument contains otherchars that can be included in word
|
|
884 many times.
|
|
885
|
|
886 Word syntax described by `ispell-dictionary-alist' (which see)."
|
|
887 (let* ((flyspell-casechars (flyspell-get-casechars))
|
|
888 (flyspell-not-casechars (flyspell-get-not-casechars))
|
|
889 (ispell-otherchars (ispell-get-otherchars))
|
|
890 (ispell-many-otherchars-p (ispell-get-many-otherchars-p))
|
|
891 (word-regexp (concat flyspell-casechars
|
|
892 "+\\("
|
|
893 ispell-otherchars
|
|
894 "?"
|
|
895 flyspell-casechars
|
|
896 "+\\)"
|
|
897 (if ispell-many-otherchars-p
|
|
898 "*" "?")))
|
|
899 (tex-prelude "[\\\\{]")
|
|
900 (tex-regexp (if (eq ispell-parser 'tex)
|
|
901 (concat tex-prelude "?" word-regexp "}?")
|
|
902 word-regexp))
|
|
903
|
|
904 did-it-once
|
|
905 start end word)
|
|
906 ;; find the word
|
|
907 (if (not (or (looking-at flyspell-casechars)
|
|
908 (and (eq ispell-parser 'tex)
|
|
909 (looking-at tex-prelude))))
|
|
910 (if following
|
|
911 (re-search-forward flyspell-casechars (point-max) t)
|
|
912 (re-search-backward flyspell-casechars (point-min) t)))
|
|
913 ;; move to front of word
|
|
914 (re-search-backward flyspell-not-casechars (point-min) 'start)
|
|
915 (let ((pos nil))
|
|
916 (while (and (looking-at ispell-otherchars)
|
|
917 (not (bobp))
|
|
918 (or (not did-it-once)
|
|
919 ispell-many-otherchars-p)
|
|
920 (not (eq pos (point))))
|
|
921 (setq pos (point))
|
|
922 (setq did-it-once t)
|
|
923 (backward-char 1)
|
|
924 (if (looking-at flyspell-casechars)
|
|
925 (re-search-backward flyspell-not-casechars (point-min) 'move)
|
|
926 (backward-char -1))))
|
|
927 ;; Now mark the word and save to string.
|
|
928 (if (eq (re-search-forward tex-regexp (point-max) t) nil)
|
|
929 nil
|
|
930 (progn
|
|
931 (setq start (match-beginning 0)
|
|
932 end (point)
|
|
933 word (buffer-substring start end))
|
|
934 (list word start end)))))
|
|
935
|
|
936 ;*---------------------------------------------------------------------*/
|
|
937 ;* flyspell-region ... */
|
|
938 ;*---------------------------------------------------------------------*/
|
|
939 (defun flyspell-region (beg end)
|
|
940 "Flyspell text between BEG and END."
|
|
941 (interactive "r")
|
|
942 (save-excursion
|
|
943 (goto-char beg)
|
|
944 (while (< (point) end)
|
|
945 (message "Spell Checking...%d%%" (* 100 (/ (float (point)) (- end beg))))
|
|
946 (flyspell-word)
|
|
947 (let ((cur (point)))
|
|
948 (forward-word 1)
|
|
949 (if (and (< (point) end) (> (point) (+ cur 1)))
|
|
950 (backward-char 1))))
|
|
951 (backward-char 1)
|
|
952 (message "Spell Checking...done")
|
|
953 (flyspell-word)))
|
|
954
|
|
955 ;*---------------------------------------------------------------------*/
|
|
956 ;* flyspell-buffer ... */
|
|
957 ;*---------------------------------------------------------------------*/
|
|
958 (defun flyspell-buffer ()
|
|
959 "Flyspell whole buffer."
|
|
960 (interactive)
|
|
961 (flyspell-region (point-min) (point-max)))
|
|
962
|
|
963 ;*---------------------------------------------------------------------*/
|
|
964 ;* flyspell-overlay-p ... */
|
|
965 ;*---------------------------------------------------------------------*/
|
|
966 (defun flyspell-overlay-p (o)
|
|
967 "A predicate that return true iff O is an overlay used by flyspell."
|
|
968 (and (overlayp o) (overlay-get o 'flyspell-overlay)))
|
|
969
|
|
970 ;*---------------------------------------------------------------------*/
|
|
971 ;* flyspell-delete-all-overlays ... */
|
|
972 ;* ------------------------------------------------------------- */
|
|
973 ;* Remove all the overlays introduced by flyspell. */
|
|
974 ;*---------------------------------------------------------------------*/
|
|
975 (defun flyspell-delete-all-overlays ()
|
|
976 "Delete all the overlays used by flyspell."
|
|
977 (let ((l (overlays-in (point-min) (point-max))))
|
|
978 (while (consp l)
|
|
979 (progn
|
|
980 (if (flyspell-overlay-p (car l))
|
|
981 (delete-overlay (car l)))
|
|
982 (setq l (cdr l))))))
|
|
983
|
|
984 ;*---------------------------------------------------------------------*/
|
|
985 ;* flyspell-unhighlight-at ... */
|
|
986 ;*---------------------------------------------------------------------*/
|
|
987 (defun flyspell-unhighlight-at (pos)
|
|
988 "Remove the flyspell overlay that are located at POS."
|
|
989 (if flyspell-persistent-highlight
|
|
990 (let ((overlays (overlays-at pos)))
|
|
991 (while (consp overlays)
|
|
992 (if (flyspell-overlay-p (car overlays))
|
|
993 (delete-overlay (car overlays)))
|
|
994 (setq overlays (cdr overlays))))
|
|
995 (delete-overlay flyspell-overlay)))
|
|
996
|
|
997 ;*---------------------------------------------------------------------*/
|
|
998 ;* flyspell-properties-at-p ... */
|
|
999 ;* ------------------------------------------------------------- */
|
|
1000 ;* Is there an highlight properties at position pos? */
|
|
1001 ;*---------------------------------------------------------------------*/
|
|
1002 (defun flyspell-properties-at-p (beg)
|
|
1003 "Return the text property at position BEG."
|
|
1004 (let ((prop (text-properties-at beg))
|
|
1005 (keep t))
|
|
1006 (while (and keep (consp prop))
|
|
1007 (if (and (eq (car prop) 'local-map) (consp (cdr prop)))
|
|
1008 (setq prop (cdr (cdr prop)))
|
|
1009 (setq keep nil)))
|
|
1010 (consp prop)))
|
|
1011
|
|
1012 ;*---------------------------------------------------------------------*/
|
|
1013 ;* make-flyspell-overlay ... */
|
|
1014 ;*---------------------------------------------------------------------*/
|
|
1015 (defun make-flyspell-overlay (beg end face mouse-face)
|
|
1016 "Allocate a new flyspell overlay that will be used to hilight
|
|
1017 an incorrect word."
|
|
1018 (let ((flyspell-overlay (make-overlay beg end)))
|
|
1019 (overlay-put flyspell-overlay 'face face)
|
|
1020 (overlay-put flyspell-overlay 'mouse-face mouse-face)
|
|
1021 (overlay-put flyspell-overlay 'flyspell-overlay t)
|
|
1022 (if (eq flyspell-emacs 'xemacs)
|
|
1023 (overlay-put flyspell-overlay
|
|
1024 flyspell-overlay-keymap-property-name
|
|
1025 flyspell-mouse-map))))
|
|
1026
|
|
1027 ;*---------------------------------------------------------------------*/
|
|
1028 ;* flyspell-highlight-incorrect-region ... */
|
|
1029 ;*---------------------------------------------------------------------*/
|
|
1030 (defun flyspell-highlight-incorrect-region (beg end)
|
|
1031 "The setup of an overlay on a region (starting at BEG and ending at END)
|
|
1032 that corresponds to an incorrect word."
|
|
1033 (run-hook-with-args 'flyspell-incorrect-hook beg end)
|
|
1034 (if (or (not (flyspell-properties-at-p beg)) flyspell-highlight-properties)
|
|
1035 (progn
|
|
1036 ;; we cleanup current overlay at the same position
|
|
1037 (if (and (not flyspell-persistent-highlight)
|
|
1038 (overlayp flyspell-overlay))
|
|
1039 (delete-overlay flyspell-overlay)
|
|
1040 (let ((overlays (overlays-at beg)))
|
|
1041 (while (consp overlays)
|
|
1042 (if (flyspell-overlay-p (car overlays))
|
|
1043 (delete-overlay (car overlays)))
|
|
1044 (setq overlays (cdr overlays)))))
|
|
1045 ;; now we can use a new overlay
|
|
1046 (setq flyspell-overlay
|
|
1047 (make-flyspell-overlay beg end
|
|
1048 'flyspell-incorrect-face 'highlight)))))
|
|
1049
|
|
1050 ;*---------------------------------------------------------------------*/
|
|
1051 ;* flyspell-highlight-duplicate-region ... */
|
|
1052 ;*---------------------------------------------------------------------*/
|
|
1053 (defun flyspell-highlight-duplicate-region (beg end)
|
|
1054 "The setup of an overlay on a region (starting at BEG and ending at END)
|
|
1055 that corresponds to an duplicated word."
|
|
1056 (if (or (not (flyspell-properties-at-p beg)) flyspell-highlight-properties)
|
|
1057 (progn
|
|
1058 ;; we cleanup current overlay at the same position
|
|
1059 (if (and (not flyspell-persistent-highlight)
|
|
1060 (overlayp flyspell-overlay))
|
|
1061 (delete-overlay flyspell-overlay)
|
|
1062 (let ((overlays (overlays-at beg)))
|
|
1063 (while (consp overlays)
|
|
1064 (if (flyspell-overlay-p (car overlays))
|
|
1065 (delete-overlay (car overlays)))
|
|
1066 (setq overlays (cdr overlays)))))
|
|
1067 ;; now we can use a new overlay
|
|
1068 (setq flyspell-overlay
|
|
1069 (make-flyspell-overlay beg end
|
|
1070 'flyspell-duplicate-face 'highlight)))))
|
|
1071
|
|
1072 ;*---------------------------------------------------------------------*/
|
|
1073 ;* flyspell-auto-correct-cache ... */
|
|
1074 ;*---------------------------------------------------------------------*/
|
|
1075 (defvar flyspell-auto-correct-pos nil)
|
|
1076 (defvar flyspell-auto-correct-region nil)
|
|
1077 (defvar flyspell-auto-correct-ring nil)
|
|
1078
|
|
1079 ;*---------------------------------------------------------------------*/
|
|
1080 ;* flyspell-auto-correct-word ... */
|
|
1081 ;*---------------------------------------------------------------------*/
|
|
1082 (defun flyspell-auto-correct-word (pos)
|
|
1083 "Auto correct the word at position POS."
|
|
1084 (interactive "d")
|
|
1085 ;; use the correct dictionary
|
|
1086 (ispell-accept-buffer-local-defs)
|
|
1087 (if (eq flyspell-auto-correct-pos pos)
|
|
1088 ;; we have already been using the function at the same location
|
|
1089 (progn
|
|
1090 (save-excursion
|
|
1091 (let ((start (car flyspell-auto-correct-region))
|
|
1092 (len (cdr flyspell-auto-correct-region)))
|
|
1093 (delete-region start (+ start len))
|
|
1094 (setq flyspell-auto-correct-ring (cdr flyspell-auto-correct-ring))
|
|
1095 (let* ((word (car flyspell-auto-correct-ring))
|
|
1096 (len (length word)))
|
|
1097 (rplacd flyspell-auto-correct-region len)
|
|
1098 (goto-char start)
|
|
1099 (insert word))))
|
|
1100 (setq flyspell-auto-correct-pos (point)))
|
|
1101 ;; retain cursor location
|
|
1102 (let ((cursor-location pos)
|
|
1103 (word (flyspell-get-word nil))
|
|
1104 start end poss)
|
|
1105 ;; destructure return word info list.
|
|
1106 (setq start (car (cdr word))
|
|
1107 end (car (cdr (cdr word)))
|
|
1108 word (car word))
|
|
1109 ;; now check spelling of word.
|
|
1110 (process-send-string ispell-process "%\n") ;put in verbose mode
|
|
1111 (process-send-string ispell-process (concat "^" word "\n"))
|
|
1112 ;; wait until ispell has processed word
|
|
1113 (while (progn
|
|
1114 (accept-process-output ispell-process)
|
|
1115 (not (string= "" (car ispell-filter)))))
|
|
1116 (setq ispell-filter (cdr ispell-filter))
|
|
1117 (if (listp ispell-filter)
|
|
1118 (setq poss (ispell-parse-output (car ispell-filter))))
|
|
1119 (cond ((or (eq poss t) (stringp poss))
|
|
1120 ;; don't correct word
|
|
1121 t)
|
|
1122 ((null poss)
|
|
1123 ;; ispell error
|
|
1124 (error "Ispell: error in Ispell process"))
|
|
1125 (t
|
|
1126 ;; the word is incorrect, we have to propose a replacement
|
|
1127 (let ((replacements (if flyspell-sort-corrections
|
|
1128 (sort (car (cdr (cdr poss))) 'string<)
|
|
1129 (car (cdr (cdr poss))))))
|
|
1130 (if (consp replacements)
|
|
1131 (progn
|
|
1132 (let ((replace (car replacements)))
|
|
1133 (setq word replace)
|
|
1134 (setq cursor-location (+ (- (length word) (- end start))
|
|
1135 cursor-location))
|
|
1136 (if (not (equal word (car poss)))
|
|
1137 (progn
|
|
1138 ;; the save the current replacements
|
|
1139 (setq flyspell-auto-correct-pos cursor-location)
|
|
1140 (setq flyspell-auto-correct-region
|
|
1141 (cons start (length word)))
|
|
1142 (let ((l replacements))
|
|
1143 (while (consp (cdr l))
|
|
1144 (setq l (cdr l)))
|
|
1145 (rplacd l (cons (car poss) replacements)))
|
|
1146 (setq flyspell-auto-correct-ring
|
|
1147 (cdr replacements))
|
|
1148 (delete-region start end)
|
|
1149 (insert word)))))))))
|
|
1150 ;; return to original location
|
|
1151 (goto-char cursor-location)
|
|
1152 (ispell-pdict-save t))))
|
|
1153
|
|
1154 ;*---------------------------------------------------------------------*/
|
|
1155 ;* flyspell-correct-word ... */
|
|
1156 ;*---------------------------------------------------------------------*/
|
|
1157 (defun flyspell-correct-word (event)
|
|
1158 "Check spelling of word under or before the cursor.
|
|
1159 If the word is not found in dictionary, display possible corrections
|
|
1160 in a popup menu allowing you to choose one.
|
|
1161
|
|
1162 Word syntax described by `ispell-dictionary-alist' (which see).
|
|
1163
|
|
1164 This will check or reload the dictionary. Use \\[ispell-change-dictionary]
|
|
1165 or \\[ispell-region] to update the Ispell process."
|
|
1166 (interactive "e")
|
|
1167 (if (eq flyspell-emacs 'xemacs)
|
|
1168 (flyspell-correct-word/mouse-keymap event)
|
|
1169 (flyspell-correct-word/local-keymap event)))
|
|
1170
|
|
1171 ;*---------------------------------------------------------------------*/
|
|
1172 ;* flyspell-correct-word/local-keymap ... */
|
|
1173 ;*---------------------------------------------------------------------*/
|
|
1174 (defun flyspell-correct-word/local-keymap (event)
|
|
1175 "emacs 19.xx seems to be buggous. Overlay keymap does not seems
|
|
1176 to work correctly with local map. That is, if a key is not
|
|
1177 defined for the overlay keymap, the current local map, is not
|
|
1178 checked. The binding is resolved with the global map. The
|
|
1179 consequence is that we can not use overlay map with flyspell."
|
|
1180 (interactive "e")
|
|
1181 (save-window-excursion
|
|
1182 (let ((save (point)))
|
|
1183 (mouse-set-point event)
|
|
1184 ;; we look for a flyspell overlay here
|
|
1185 (let ((overlays (overlays-at (point)))
|
|
1186 (overlay nil))
|
|
1187 (while (consp overlays)
|
|
1188 (if (flyspell-overlay-p (car overlays))
|
|
1189 (progn
|
|
1190 (setq overlay (car overlays))
|
|
1191 (setq overlays nil))
|
|
1192 (setq overlays (cdr overlays))))
|
|
1193 ;; we return to the correct location
|
|
1194 (goto-char save)
|
|
1195 ;; we check to see if button2 has been used overlay a
|
|
1196 ;; flyspell overlay
|
|
1197 (if overlay
|
|
1198 ;; yes, so we use the flyspell function
|
|
1199 (flyspell-correct-word/mouse-keymap event)
|
|
1200 ;; no so we have to use the non flyspell binding
|
|
1201 (let ((flyspell-mode nil))
|
|
1202 (if (key-binding (this-command-keys))
|
|
1203 (command-execute (key-binding (this-command-keys))))))))))
|
|
1204
|
|
1205 ;*---------------------------------------------------------------------*/
|
|
1206 ;* flyspell-correct-word ... */
|
|
1207 ;*---------------------------------------------------------------------*/
|
|
1208 (defun flyspell-correct-word/mouse-keymap (event)
|
|
1209 "Popup a menu to present possible correction. The word checked is the
|
|
1210 word at the mouse position."
|
|
1211 (interactive "e")
|
|
1212 ;; use the correct dictionary
|
|
1213 (ispell-accept-buffer-local-defs)
|
|
1214 ;; retain cursor location (I don't know why but save-excursion here fails).
|
|
1215 (let ((save (point)))
|
|
1216 (mouse-set-point event)
|
|
1217 (let ((cursor-location (point))
|
|
1218 (word (flyspell-get-word nil))
|
|
1219 start end poss replace)
|
|
1220 ;; destructure return word info list.
|
|
1221 (setq start (car (cdr word))
|
|
1222 end (car (cdr (cdr word)))
|
|
1223 word (car word))
|
|
1224 ;; now check spelling of word.
|
|
1225 (process-send-string ispell-process "%\n") ;put in verbose mode
|
|
1226 (process-send-string ispell-process (concat "^" word "\n"))
|
|
1227 ;; wait until ispell has processed word
|
|
1228 (while (progn
|
|
1229 (accept-process-output ispell-process)
|
|
1230 (not (string= "" (car ispell-filter)))))
|
|
1231 (setq ispell-filter (cdr ispell-filter))
|
|
1232 (if (listp ispell-filter)
|
|
1233 (setq poss (ispell-parse-output (car ispell-filter))))
|
|
1234 (cond ((or (eq poss t) (stringp poss))
|
|
1235 ;; don't correct word
|
|
1236 t)
|
|
1237 ((null poss)
|
|
1238 ;; ispell error
|
|
1239 (error "Ispell: error in Ispell process"))
|
|
1240 ((string-match "GNU" (emacs-version))
|
|
1241 ;; the word is incorrect, we have to propose a replacement
|
|
1242 (setq replace (flyspell-gnuemacs-popup event poss word))
|
|
1243 (cond ((eq replace 'ignore)
|
|
1244 nil)
|
|
1245 ((eq replace 'save)
|
|
1246 (process-send-string ispell-process (concat "*" word "\n"))
|
|
1247 (flyspell-unhighlight-at cursor-location)
|
|
1248 (setq ispell-pdict-modified-p '(t)))
|
|
1249 ((or (eq replace 'buffer) (eq replace 'session))
|
|
1250 (process-send-string ispell-process (concat "@" word "\n"))
|
|
1251 (if (null ispell-pdict-modified-p)
|
|
1252 (setq ispell-pdict-modified-p
|
|
1253 (list ispell-pdict-modified-p)))
|
|
1254 (flyspell-unhighlight-at cursor-location)
|
|
1255 (if (eq replace 'buffer)
|
|
1256 (ispell-add-per-file-word-list word)))
|
|
1257 (replace
|
|
1258 (setq word (if (atom replace) replace (car replace))
|
|
1259 cursor-location (+ (- (length word) (- end start))
|
|
1260 cursor-location))
|
|
1261 (if (not (equal word (car poss)))
|
|
1262 (progn
|
|
1263 (delete-region start end)
|
|
1264 (insert word))))))
|
|
1265 ((string-match "XEmacs" (emacs-version))
|
|
1266 (flyspell-xemacs-popup
|
|
1267 event poss word cursor-location start end)))
|
|
1268 (ispell-pdict-save t))
|
|
1269 (if (< save (point-max))
|
|
1270 (goto-char save)
|
|
1271 (goto-char (point-max)))))
|
|
1272
|
|
1273 ;*---------------------------------------------------------------------*/
|
|
1274 ;* flyspell-xemacs-correct ... */
|
|
1275 ;*---------------------------------------------------------------------*/
|
|
1276 (defun flyspell-xemacs-correct (replace poss word cursor-location start end)
|
|
1277 "The xemacs popup menu callback."
|
|
1278 (cond ((eq replace 'ignore)
|
|
1279 nil)
|
|
1280 ((eq replace 'save)
|
|
1281 (process-send-string ispell-process (concat "*" word "\n"))
|
|
1282 (flyspell-unhighlight-at cursor-location)
|
|
1283 (setq ispell-pdict-modified-p '(t)))
|
|
1284 ((or (eq replace 'buffer) (eq replace 'session))
|
|
1285 (process-send-string ispell-process (concat "@" word "\n"))
|
|
1286 (flyspell-unhighlight-at cursor-location)
|
|
1287 (if (null ispell-pdict-modified-p)
|
|
1288 (setq ispell-pdict-modified-p
|
|
1289 (list ispell-pdict-modified-p)))
|
|
1290 (if (eq replace 'buffer)
|
|
1291 (ispell-add-per-file-word-list word)))
|
|
1292 (replace
|
|
1293 (setq word (if (atom replace) replace (car replace))
|
|
1294 cursor-location (+ (- (length word) (- end start))
|
|
1295 cursor-location))
|
|
1296 (if (not (equal word (car poss)))
|
|
1297 (save-excursion
|
|
1298 (delete-region start end)
|
|
1299 (goto-char start)
|
|
1300 (insert word))))))
|
|
1301
|
|
1302 ;*---------------------------------------------------------------------*/
|
|
1303 ;* flyspell-gnuemacs-popup */
|
|
1304 ;*---------------------------------------------------------------------*/
|
|
1305 (defun flyspell-gnuemacs-popup (event poss word)
|
|
1306 "The gnu-emacs popup menu."
|
|
1307 (if (not event)
|
|
1308 (let* ((mouse-pos (mouse-position))
|
|
1309 (mouse-pos (if (nth 1 mouse-pos)
|
|
1310 mouse-pos
|
|
1311 (set-mouse-position (car mouse-pos)
|
|
1312 (/ (frame-width) 2) 2)
|
|
1313 (unfocus-frame)
|
|
1314 (mouse-position))))
|
|
1315 (setq event (list (list (car (cdr mouse-pos))
|
|
1316 (1+ (cdr (cdr mouse-pos))))
|
|
1317 (car mouse-pos)))))
|
|
1318 (let* ((corrects (if flyspell-sort-corrections
|
|
1319 (sort (car (cdr (cdr poss))) 'string<)
|
|
1320 (car (cdr (cdr poss)))))
|
|
1321 (cor-menu (if (consp corrects)
|
|
1322 (mapcar (lambda (correct)
|
|
1323 (list correct correct))
|
|
1324 corrects)
|
|
1325 '()))
|
|
1326 (affix (car (cdr (cdr (cdr poss)))))
|
|
1327 (base-menu (let ((save (if (consp affix)
|
|
1328 (list
|
|
1329 (list (concat "Save affix: " (car affix))
|
|
1330 'save)
|
|
1331 '("Accept (session)" accept)
|
|
1332 '("Accept (buffer)" buffer))
|
|
1333 '(("Save word" save)
|
|
1334 ("Accept (session)" session)
|
|
1335 ("Accept (buffer)" buffer)))))
|
|
1336 (if (consp cor-menu)
|
|
1337 (append cor-menu (cons "" save))
|
|
1338 save)))
|
|
1339 (menu (cons "flyspell correction menu" base-menu)))
|
|
1340 (car (x-popup-menu event
|
|
1341 (list (format "%s [%s]" word (or ispell-local-dictionary
|
|
1342 ispell-dictionary))
|
|
1343 menu)))))
|
|
1344
|
|
1345 ;*---------------------------------------------------------------------*/
|
|
1346 ;* flyspell-xemacs-popup */
|
|
1347 ;*---------------------------------------------------------------------*/
|
|
1348 (defun flyspell-xemacs-popup (event poss word cursor-location start end)
|
|
1349 "The xemacs popup menu."
|
|
1350 (let* ((corrects (if flyspell-sort-corrections
|
|
1351 (sort (car (cdr (cdr poss))) 'string<)
|
|
1352 (car (cdr (cdr poss)))))
|
|
1353 (cor-menu (if (consp corrects)
|
|
1354 (mapcar (lambda (correct)
|
|
1355 (vector correct
|
|
1356 (list 'flyspell-xemacs-correct
|
|
1357 correct
|
|
1358 (list 'quote poss)
|
|
1359 word
|
|
1360 cursor-location
|
|
1361 start
|
|
1362 end)
|
|
1363 t))
|
|
1364 corrects)
|
|
1365 '()))
|
|
1366 (affix (car (cdr (cdr (cdr poss)))))
|
|
1367 (menu (let ((save (if (consp affix)
|
|
1368 (vector
|
|
1369 (concat "Save affix: " (car affix))
|
|
1370 (list 'flyspell-xemacs-correct
|
|
1371 ''save
|
|
1372 (list 'quote poss)
|
|
1373 word
|
|
1374 cursor-location
|
|
1375 start
|
|
1376 end)
|
|
1377 t)
|
|
1378 (vector
|
|
1379 "Save word"
|
|
1380 (list 'flyspell-xemacs-correct
|
|
1381 ''save
|
|
1382 (list 'quote poss)
|
|
1383 word
|
|
1384 cursor-location
|
|
1385 start
|
|
1386 end)
|
|
1387 t)))
|
|
1388 (session (vector "Accept (session)"
|
|
1389 (list 'flyspell-xemacs-correct
|
|
1390 ''session
|
|
1391 (list 'quote poss)
|
|
1392 word
|
|
1393 cursor-location
|
|
1394 start
|
|
1395 end)
|
|
1396 t))
|
|
1397 (buffer (vector "Accept (buffer)"
|
|
1398 (list 'flyspell-xemacs-correct
|
|
1399 ''buffer
|
|
1400 (list 'quote poss)
|
|
1401 word
|
|
1402 cursor-location
|
|
1403 start
|
|
1404 end)
|
|
1405 t)))
|
|
1406 (if (consp cor-menu)
|
|
1407 (append cor-menu (list "-" save session buffer))
|
|
1408 (list save session buffer)))))
|
|
1409 (popup-menu (cons (format "%s [%s]" word (or ispell-local-dictionary
|
|
1410 ispell-dictionary))
|
|
1411 menu))))
|
|
1412
|
|
1413 (provide 'flyspell)
|
|
1414
|
|
1415 ;;; flyspell.el ends here
|