X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/5c823193ae0d707c4716fdd87643b707dcaa53cc..08fea928870ca809d9d4cae2b6b9f829cb6280b4:/lisp/textmodes/flyspell.el diff --git a/lisp/textmodes/flyspell.el b/lisp/textmodes/flyspell.el index ebee4691e8..9d5c7868d1 100644 --- a/lisp/textmodes/flyspell.el +++ b/lisp/textmodes/flyspell.el @@ -1,7 +1,7 @@ ;;; flyspell.el --- on-the-fly spell checker -;; Copyright (C) 1998, 2000, 2002, 2003, 2004, -;; 2005, 2006 Free Software Foundation, Inc. +;; Copyright (C) 1998, 2000, 2001, 2002, 2003, 2004, +;; 2005, 2006, 2007 Free Software Foundation, Inc. ;; Author: Manuel Serrano ;; Maintainer: FSF @@ -11,7 +11,7 @@ ;; 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) +;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; GNU Emacs is distributed in the hope that it will be useful, @@ -67,11 +67,21 @@ Non-nil means use highlight, nil means use minibuffer messages." (defcustom flyspell-mark-duplications-flag t "Non-nil means Flyspell reports a repeated word as an error. +See `flyspell-mark-duplications-exceptions' to add exceptions to this rule. Detection of repeated words is not implemented in \"large\" regions; see `flyspell-large-region'." :group 'flyspell :type 'boolean) +(defcustom flyspell-mark-duplications-exceptions + '(("francais" . ("nous" "vous"))) + "A list of exceptions for duplicated words. +It should be a list of (LANGUAGE . EXCEPTION-LIST). LANGUAGE is matched +against the current dictionary and EXCEPTION-LIST is a list of strings. +The duplicated word is downcased before it is compared with the exceptions." + :group 'flyspell + :type '(alist :key-type string :value-type (repeat string))) + (defcustom flyspell-sort-corrections nil "Non-nil means, sort the corrections alphabetically before popping them." :group 'flyspell @@ -189,7 +199,7 @@ Ispell's ultimate default dictionary." :type 'string) (defcustom flyspell-check-tex-math-command nil - "Non nil means check even inside TeX math environment. + "Non-nil means check even inside TeX math environment. TeX math environments are discovered by the TEXMATHP that implemented inside the texmathp.el Emacs package. That package may be found at: http://strw.leidenuniv.nl/~dominik/Tools" @@ -412,6 +422,7 @@ property of the major mode name.") (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) + (define-key map [?\C-c ?$] 'flyspell-correct-word-before-point) map) "Minor mode keymap for Flyspell mode--for the whole buffer.") @@ -430,7 +441,7 @@ property of the major mode name.") (defface flyspell-incorrect '((((class color)) (:foreground "OrangeRed" :bold t :underline t)) (t (:bold t))) - "Face used to display a misspelled word in Flyspell." + "Face used for marking a misspelled word in Flyspell." :group 'flyspell) ;; backward-compatibility alias (put 'flyspell-incorrect-face 'face-alias 'flyspell-incorrect) @@ -438,7 +449,7 @@ property of the major mode name.") (defface flyspell-duplicate '((((class color)) (:foreground "Gold3" :bold t :underline t)) (t (:bold t))) - "Face used to display subsequent occurrences of a misspelled word. + "Face used for marking a misspelled word that appears twice in the buffer. See also `flyspell-duplicate-distance'." :group 'flyspell) ;; backward-compatibility alias @@ -456,7 +467,8 @@ See also `flyspell-duplicate-distance'." This spawns a single Ispell process and checks each word. 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. +With a prefix argument ARG, turn Flyspell minor mode on if ARG is positive, +otherwise turn it off. Bindings: \\[ispell-word]: correct words (using Ispell). @@ -540,6 +552,11 @@ in your .emacs file. (member (or ispell-local-dictionary ispell-dictionary) flyspell-dictionaries-that-consider-dash-as-word-delimiter))))) +(defun flyspell-hack-local-variables-hook () + ;; When local variables are loaded, see if the dictionary context + ;; has changed. + (flyspell-accept-buffer-local-defs 'force)) + (defun flyspell-kill-ispell-hook () (setq flyspell-last-buffer nil) (dolist (buf (buffer-list)) @@ -578,6 +595,9 @@ in your .emacs file. (add-hook 'pre-command-hook (function flyspell-pre-command-hook) t t) ;; we bound flyspell action to after-change hook (add-hook 'after-change-functions 'flyspell-after-change-function nil t) + ;; we bound flyspell action to hack-local-variables-hook + (add-hook 'hack-local-variables-hook + (function flyspell-hack-local-variables-hook) t t) ;; set flyspell-generic-check-word-predicate based on the major mode (let ((mode-predicate (get major-mode 'flyspell-mode-predicate))) (if mode-predicate @@ -683,6 +703,8 @@ not the very same deplacement command." (remove-hook 'post-command-hook (function flyspell-post-command-hook) t) (remove-hook 'pre-command-hook (function flyspell-pre-command-hook) t) (remove-hook 'after-change-functions 'flyspell-after-change-function t) + (remove-hook 'hack-local-variables-hook + (function flyspell-hack-local-variables-hook) t) ;; we remove all the flyspell hilightings (flyspell-delete-all-overlays) ;; we have to erase pre cache variables @@ -959,6 +981,7 @@ Mostly we check word delimiters." (defun flyspell-word-search-backward (word bound) (save-excursion (let ((r '()) + (inhibit-point-motion-hooks t) p) (while (and (not r) (setq p (search-backward word bound t))) (let ((lw (flyspell-get-word '()))) @@ -973,6 +996,7 @@ Mostly we check word delimiters." (defun flyspell-word-search-forward (word bound) (save-excursion (let ((r '()) + (inhibit-point-motion-hooks t) p) (while (and (not r) (setq p (search-forward word bound t))) (let ((lw (flyspell-get-word '()))) @@ -1008,12 +1032,22 @@ Mostly we check word delimiters." (and (> start (point-min)) (not (memq (char-after (1- start)) '(?\} ?\\))))) flyspell-mark-duplications-flag + (not (catch 'exception + (dolist (except flyspell-mark-duplications-exceptions) + (and (string= (or ispell-local-dictionary + ispell-dictionary) + (car except)) + (member (downcase word) (cdr except)) + (throw 'exception t))))) (save-excursion - (goto-char (1- start)) - (let ((p (flyspell-word-search-backward - word - (- start (1+ (- end start)))))) - (and p (/= p (1- start)))))) + (goto-char start) + (let* ((bound + (- start + (- end start) + (- (skip-chars-backward " \t\n\f")))) + (p (when (>= bound (point-min)) + (flyspell-word-search-backward word bound)))) + (and p (/= p start))))) ;; yes, this is a doublon (flyspell-highlight-incorrect-region start end 'doublon) nil) @@ -1470,7 +1504,7 @@ The buffer to mark them in is `flyspell-large-region-buffer'." (flyspell-word) ; Make sure current word is checked (backward-word 1) (while (and (< (point) end) - (re-search-forward "\\b\\([^ \n\t]+\\)[ \n\t]+\\1\\b" + (re-search-forward "\\<\\(\\w+\\)\\>[ \n\t\f]+\\1\\>" end 'move)) (flyspell-word) (backward-word 1)) @@ -1493,7 +1527,7 @@ The buffer to mark them in is `flyspell-large-region-buffer'." (if flyspell-issue-message-flag (message "Checking region...")) (set-buffer curbuf) (ispell-check-version) - (let ((c (apply 'call-process-region beg + (let ((c (apply 'ispell-call-process-region beg end ispell-program-name nil @@ -1605,7 +1639,7 @@ FLYSPELL-BUFFER." ;;* flyspell-overlay-p ... */ ;;*---------------------------------------------------------------------*/ (defun flyspell-overlay-p (o) - "A predicate that return true iff O is an overlay used by flyspell." + "Return true if O is an overlay used by flyspell." (and (overlayp o) (overlay-get o 'flyspell-overlay))) ;;*---------------------------------------------------------------------*/ @@ -1706,7 +1740,9 @@ is itself incorrect, but suspiciously repeated." ;; now we can use a new overlay (setq flyspell-overlay (make-flyspell-overlay - beg end 'flyspell-incorrect 'highlight))))))) + beg end + (if (eq poss 'doublon) 'flyspell-duplicate 'flyspell-incorrect) + 'highlight))))))) ;;*---------------------------------------------------------------------*/ ;;* flyspell-highlight-duplicate-region ... */ @@ -1809,7 +1845,7 @@ misspelled words backwards." (defun flyspell-define-abbrev (name expansion) (let ((table (flyspell-abbrev-table))) (when table - (define-abbrev table name expansion)))) + (define-abbrev table (downcase name) expansion)))) ;;*---------------------------------------------------------------------*/ ;;* flyspell-auto-correct-word ... */ @@ -1943,12 +1979,8 @@ Sets `flyspell-auto-correct-previous-pos' to nil" But don't look beyond what's visible on the screen." (interactive "d") - (let (top bot) - (save-excursion - (move-to-window-line 0) - (setq top (point)) - (move-to-window-line -1) - (setq bot (point))) + (let ((top (window-start)) + (bot (window-end))) (save-excursion (save-restriction (narrow-to-region top bot) @@ -1992,52 +2024,63 @@ But don't look beyond what's visible on the screen." ;;*---------------------------------------------------------------------*/ ;;* flyspell-correct-word ... */ ;;*---------------------------------------------------------------------*/ + (defun flyspell-correct-word (event) "Pop up a menu of possible corrections for a misspelled word. The word checked is the word at the mouse position." (interactive "e") - ;; use the correct dictionary - (flyspell-accept-buffer-local-defs) - ;; retain cursor location (I don't know why but save-excursion here fails). (let ((save (point))) (mouse-set-point event) - (let ((cursor-location (point)) - (word (flyspell-get-word nil))) - (if (consp word) - (let ((start (car (cdr word))) - (end (car (cdr (cdr word)))) - (word (car word)) - poss ispell-filter) - ;; now check spelling of word. - (ispell-send-string "%\n") ;put in verbose mode - (ispell-send-string (concat "^" word "\n")) - ;; wait until ispell has processed word - (while (progn - (accept-process-output ispell-process) - (not (string= "" (car ispell-filter))))) - ;; Remove leading empty element - (setq ispell-filter (cdr ispell-filter)) - ;; ispell process should return something after word is sent. - ;; Tag word as valid (i.e., skip) otherwise - (or ispell-filter - (setq ispell-filter '(*))) - (if (consp ispell-filter) - (setq poss (ispell-parse-output (car ispell-filter)))) - (cond - ((or (eq poss t) (stringp poss)) - ;; don't correct word - t) - ((null poss) - ;; ispell error - (error "Ispell: error in Ispell process")) - ((featurep 'xemacs) - (flyspell-xemacs-popup - poss word cursor-location start end save)) - (t - ;; The word is incorrect, we have to propose a replacement. - (flyspell-do-correct (flyspell-emacs-popup event poss word) - poss word cursor-location start end save))) - (ispell-pdict-save t)))))) + (flyspell-correct-word-before-point event save))) + +(defun flyspell-correct-word-before-point (&optional event opoint) + "Pop up a menu of possible corrections for misspelled word before point. +If EVENT is non-nil, it is the mouse event that invoked this operation; +that controls where to put the menu. +If OPOINT is non-nil, restore point there after adjusting it for replacement." + (interactive) + (unless (mouse-position) + (error "Pop-up menus do not work on this terminal")) + ;; use the correct dictionary + (flyspell-accept-buffer-local-defs) + (or opoint (setq opoint (point-marker))) + (let ((cursor-location (point)) + (word (flyspell-get-word nil))) + (if (consp word) + (let ((start (car (cdr word))) + (end (car (cdr (cdr word)))) + (word (car word)) + poss ispell-filter) + ;; now check spelling of word. + (ispell-send-string "%\n") ;put in verbose mode + (ispell-send-string (concat "^" word "\n")) + ;; wait until ispell has processed word + (while (progn + (accept-process-output ispell-process) + (not (string= "" (car ispell-filter))))) + ;; Remove leading empty element + (setq ispell-filter (cdr ispell-filter)) + ;; ispell process should return something after word is sent. + ;; Tag word as valid (i.e., skip) otherwise + (or ispell-filter + (setq ispell-filter '(*))) + (if (consp ispell-filter) + (setq poss (ispell-parse-output (car ispell-filter)))) + (cond + ((or (eq poss t) (stringp poss)) + ;; don't correct word + t) + ((null poss) + ;; ispell error + (error "Ispell: error in Ispell process")) + ((featurep 'xemacs) + (flyspell-xemacs-popup + poss word cursor-location start end opoint)) + (t + ;; The word is incorrect, we have to propose a replacement. + (flyspell-do-correct (flyspell-emacs-popup event poss word) + poss word cursor-location start end opoint))) + (ispell-pdict-save t))))) ;;*---------------------------------------------------------------------*/ ;;* flyspell-do-correct ... */ @@ -2109,6 +2152,8 @@ The word checked is the word at the mouse position." ;;*---------------------------------------------------------------------*/ (defun flyspell-emacs-popup (event poss word) "The Emacs popup menu." + (unless window-system + (error "This command requires pop-up dialogs")) (if (not event) (let* ((mouse-pos (mouse-position)) (mouse-pos (if (nth 1 mouse-pos)