]> code.delx.au - gnu-emacs/blobdiff - lisp/textmodes/ispell.el
Merge from origin/emacs-25
[gnu-emacs] / lisp / textmodes / ispell.el
index 498def0b966cb148f696b1b75d104e562e57b49a..284fea4acd59f7b69e84b44b5f7951dba26d3a5a 100644 (file)
@@ -1,6 +1,6 @@
-;;; ispell.el --- interface to International Ispell Versions 3.1 and 3.2
+;;; ispell.el --- interface to International Ispell Versions 3.1 and 3.2  -*- lexical-binding:t -*-
 
-;; Copyright (C) 1994-1995, 1997-2015 Free Software Foundation, Inc.
+;; Copyright (C) 1994-1995, 1997-2016 Free Software Foundation, Inc.
 
 ;; Author:           Ken Stevens <k.stevens@ieee.org>
 ;; Maintainer:       Ken Stevens <k.stevens@ieee.org>
@@ -46,9 +46,9 @@
 ;; your own dictionaries.
 
 ;;  Depending on the mail system you use, you may want to include these:
-;;  (add-hook 'news-inews-hook 'ispell-message)
-;;  (add-hook 'mail-send-hook  'ispell-message)
-;;  (add-hook 'mh-before-send-letter-hook 'ispell-message)
+;;  (add-hook 'news-inews-hook #'ispell-message)
+;;  (add-hook 'mail-send-hook  #'ispell-message)
+;;  (add-hook 'mh-before-send-letter-hook #'ispell-message)
 
 ;;   Ispell has a TeX parser and a nroff parser (the default).
 ;; The parsing is controlled by the variable ispell-parser.  Currently
 ;; Fixed bug in returning to nroff mode from tex mode.
 
 ;;; Compatibility code for XEmacs and (not too) older emacsen:
-
-(eval-and-compile ;; Protect against declare-function undefined in XEmacs
-  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
-
-(declare-function ispell-check-minver "ispell" (v1 v2))
-(declare-function ispell-looking-back "ispell"
-                 (regexp &optional limit &rest ignored))
-
-(if (fboundp 'version<=)
-    (defalias 'ispell-check-minver 'version<=)
-  (defun ispell-check-minver (minver version)
-    "Check if string VERSION is at least string MINVER.
+(defalias 'ispell-check-minver
+  (if (fboundp 'version<=) 'version<=
+    (lambda (minver version)
+      "Check if string VERSION is at least string MINVER.
 Both must be in [0-9]+.[0-9]+... format.  This is a fallback
 compatibility function in case `version<=' is not available."
-    (let ((pending t)
-         (return t)
-         start-ver start-mver)
-      ;; Loop until an absolute greater or smaller condition is reached
-      ;; or until no elements are left in any of version and minver. In
-      ;; this case version is exactly the minimal, so return OK.
-      (while pending
-       (let (ver mver)
-         (if (string-match "[0-9]+" version start-ver)
-             (setq start-ver (match-end 0)
-                   ver (string-to-number (match-string 0 version))))
-         (if (string-match "[0-9]+" minver start-mver)
-             (setq start-mver (match-end 0)
-                   mver (string-to-number (match-string 0 minver))))
-
-         (if (or ver mver)
-             (progn
-               (or ver  (setq ver 0))
-               (or mver (setq mver 0))
-               ;; If none of below conditions match, this element is the
-               ;; same. Go checking next element.
-               (if (> ver mver)
-                   (setq pending nil)
-                 (if (< ver mver)
-                     (setq pending nil
-                           return nil))))
-           (setq pending nil))))
-      return)))
+      (let ((pending t)
+            (return t)
+            start-ver start-mver)
+        ;; Loop until an absolute greater or smaller condition is reached
+        ;; or until no elements are left in any of version and minver. In
+        ;; this case version is exactly the minimal, so return OK.
+        (while pending
+          (let (ver mver)
+            (if (string-match "[0-9]+" version start-ver)
+                (setq start-ver (match-end 0)
+                      ver (string-to-number (match-string 0 version))))
+            (if (string-match "[0-9]+" minver start-mver)
+                (setq start-mver (match-end 0)
+                      mver (string-to-number (match-string 0 minver))))
+
+            (if (or ver mver)
+                (progn
+                  (or ver  (setq ver 0))
+                  (or mver (setq mver 0))
+                  ;; If none of below conditions match, this element is the
+                  ;; same. Go checking next element.
+                  (if (> ver mver)
+                      (setq pending nil)
+                    (if (< ver mver)
+                        (setq pending nil
+                              return nil))))
+              (setq pending nil))))
+        return))))
 
 ;; XEmacs does not have looking-back
-(if (fboundp 'looking-back)
-    (defalias 'ispell-looking-back 'looking-back)
-  (defun ispell-looking-back (regexp &optional limit &rest ignored)
-    "Return non-nil if text before point matches regular expression REGEXP.
+(defalias 'ispell-looking-back
+  (if (fboundp 'looking-back) 'looking-back
+    (lambda (regexp &optional limit &rest ignored)
+      "Return non-nil if text before point matches regular expression REGEXP.
 Like `looking-at' except matches before point, and is slower.
 LIMIT if non-nil speeds up the search by specifying a minimum
 starting position, to avoid checking matches that would start
@@ -251,8 +243,8 @@ before LIMIT.
 
 This is a stripped down compatibility function for use when
 full featured `looking-back' function is missing."
-    (save-excursion
-      (re-search-backward (concat "\\(?:" regexp "\\)\\=") limit t))))
+      (save-excursion
+        (re-search-backward (concat "\\(?:" regexp "\\)\\=") limit t)))))
 
 ;;; XEmacs21 does not have `with-no-warnings'. Taken from org mode.
 (defmacro ispell-with-no-warnings (&rest body)
@@ -260,6 +252,8 @@ full featured `looking-back' function is missing."
 
 ;;; Code:
 
+(eval-when-compile (require 'cl-lib))
+
 (defvar mail-yank-prefix)
 
 (defgroup ispell nil
@@ -388,7 +382,7 @@ It consists of pairs (REGEXP . DICTIONARY).  If REGEXP is found
 in the message headers, `ispell-local-dictionary' will be set to
 DICTIONARY if `ispell-local-dictionary' is not buffer-local.
 E.g. you may use the following value:
-  '((\"^Newsgroups:[ \\t]*de\\\\.\" . \"deutsch8\")
+   ((\"^Newsgroups:[ \\t]*de\\\\.\" . \"deutsch8\")
     (\"^To:[^\\n,]+\\\\.de[ \\t\\n,>]\" . \"deutsch8\"))"
   :type '(repeat (cons regexp string))
   :group 'ispell)
@@ -484,7 +478,7 @@ The function must take one string argument and return a string."
   "When non-nil ispell uses framepop to display choices in a dedicated frame.
 You can set this variable to dynamically use framepop if you are in a
 window system by evaluating the following on startup to set this variable:
-  (and window-system (condition-case () (require 'framepop) (error nil)))"
+  (and window-system (condition-case () (require \\='framepop) (error nil)))"
   :type 'boolean
   :group 'ispell)
 
@@ -492,7 +486,7 @@ window system by evaluating the following on startup to set this variable:
 (defcustom ispell-personal-dictionary nil
   "File name of your personal spelling dictionary, or nil.
 If nil, the default personal dictionary, (\"~/.ispell_DICTNAME\" for ispell or
-\"~/.aspell.LANG.pws\" for aspell) is used, where DICTNAME is the name of your
+\"~/.aspell.LANG.pws\" for Aspell) is used, where DICTNAME is the name of your
 default dictionary and LANG the two letter language code."
   :type '(choice file
                 (const :tag "default" nil))
@@ -699,8 +693,8 @@ re-start Emacs."
 
 Each element of this list is also a list:
 
-\(DICTIONARY-NAME CASECHARS NOT-CASECHARS OTHERCHARS MANY-OTHERCHARS-P
-        ISPELL-ARGS EXTENDED-CHARACTER-MODE CHARACTER-SET\)
+ (DICTIONARY-NAME CASECHARS NOT-CASECHARS OTHERCHARS MANY-OTHERCHARS-P
+        ISPELL-ARGS EXTENDED-CHARACTER-MODE CHARACTER-SET)
 
 DICTIONARY-NAME is a possible string value of variable `ispell-dictionary',
 nil means the default dictionary.
@@ -747,29 +741,29 @@ when the language uses non-ASCII characters.
 Note that with \"ispell\" as the speller, the CASECHARS and
 OTHERCHARS slots of the alist should contain the same character
 set as casechars and otherchars in the LANGUAGE.aff file \(e.g.,
-english.aff\).  aspell and hunspell don't have this limitation.")
+english.aff).  Aspell and Hunspell don't have this limitation.")
 
 (defvar ispell-really-aspell nil
-  "Non-nil if we can use aspell extensions.")
+  "Non-nil if we can use Aspell extensions.")
 (defvar ispell-really-hunspell nil
-  "Non-nil if we can use hunspell extensions.")
+  "Non-nil if we can use Hunspell extensions.")
 (defvar ispell-encoding8-command nil
   "Command line option prefix to select encoding if supported, nil otherwise.
 If setting the encoding is supported by spellchecker and is selectable from
-the command line, this variable will contain \"--encoding=\" for aspell
-and \"-i \" for hunspell, so the appropriate mime charset can be selected.
-That will be set in `ispell-check-version' for hunspell >= 1.1.6 and
-aspell >= 0.60.
+the command line, this variable will contain \"--encoding=\" for Aspell
+and \"-i \" for Hunspell, so the appropriate mime charset can be selected.
+That will be set in `ispell-check-version' for Hunspell >= 1.1.6 and
+Aspell >= 0.60.
 
-For aspell, non-nil also means to try to automatically find its dictionaries.
+For Aspell, non-nil also means to try to automatically find its dictionaries.
 
-Earlier aspell versions do not consistently support charset encoding.  Handling
+Earlier Aspell versions do not consistently support charset encoding.  Handling
 this would require some extra guessing in `ispell-aspell-find-dictionary'.")
 
 (defvar ispell-aspell-supports-utf8 nil
-  "Non-nil if aspell has consistent command line UTF-8 support.  Obsolete.
+  "Non-nil if Aspell has consistent command line UTF-8 support.  Obsolete.
 ispell.el and flyspell.el will use for this purpose the more generic
-variable `ispell-encoding8-command' for both aspell and hunspell.  Is left
+variable `ispell-encoding8-command' for both Aspell and Hunspell.  Is left
 here just for backwards compatibility.")
 
 (make-obsolete-variable 'ispell-aspell-supports-utf8
@@ -942,9 +936,11 @@ Otherwise returns the library directory name, if that is defined."
       (setq default-directory (expand-file-name "~/")))
     (apply 'call-process-region args)))
 
+(defvar ispell-debug-buffer)
+
 (defun ispell-create-debug-buffer (&optional append)
   "Create an ispell debug buffer for debugging output.
-Use APPEND to append the info to previous buffer if exists,
+If APPEND is non-nil, append the info to previous buffer if exists,
 otherwise is reset.  Returns name of ispell debug buffer.
 See `ispell-buffer-with-debug' for an example of use."
   (let ((ispell-debug-buffer (get-buffer-create "*ispell-debug*")))
@@ -956,7 +952,7 @@ See `ispell-buffer-with-debug' for an example of use."
     ispell-debug-buffer))
 
 (defsubst ispell-print-if-debug (format &rest args)
-  "Print message to `ispell-debug-buffer' buffer if enabled."
+  "Print message using FORMAT and ARGS to `ispell-debug-buffer' buffer if enabled."
   (if (boundp 'ispell-debug-buffer)
       (with-current-buffer ispell-debug-buffer
        (goto-char (point-max))
@@ -1000,7 +996,7 @@ and added as a submenu of the \"Edit\" menu.")
 (defvar ispell-async-processp (and (fboundp 'delete-process)
                                   (fboundp 'process-send-string)
                                   (fboundp 'accept-process-output)
-                                  ;;(fboundp 'start-process)
+                                  ;;(fboundp 'make-process)
                                   ;;(fboundp 'set-process-filter)
                                   ;;(fboundp 'process-kill-without-query)
                                   )
@@ -1009,13 +1005,13 @@ and added as a submenu of the \"Edit\" menu.")
 ;; Make ispell.el work better with aspell.
 
 (defvar ispell-aspell-dictionary-alist nil
-  "An alist of parsed aspell dicts and associated parameters.
+  "An alist of parsed Aspell dicts and associated parameters.
 Internal use.")
 
 (defun ispell-find-aspell-dictionaries ()
   "Find Aspell's dictionaries, and record in `ispell-dictionary-alist'."
   (unless (and ispell-really-aspell ispell-encoding8-command)
-    (error "This function only works with aspell >= 0.60"))
+    (error "This function only works with Aspell >= 0.60"))
   (let* ((dictionaries
          (split-string
           (with-temp-buffer
@@ -1053,7 +1049,7 @@ Assumes that value contains no whitespace."
     (car (split-string (buffer-string)))))
 
 (defun ispell-aspell-find-dictionary (dict-name)
-  "For aspell dictionary DICT-NAME, return a list of parameters if an
+  "For Aspell dictionary DICT-NAME, return a list of parameters if an
 associated data file is found or nil otherwise.  List format is that
 of `ispell-dictionary-base-alist' elements."
 
@@ -1075,9 +1071,11 @@ of `ispell-dictionary-base-alist' elements."
           (dolist ( tmp-path (list ispell-aspell-dict-dir
                                    ispell-aspell-data-dir ))
             ;; Try xx.dat first, strip out variant, country code, etc,
-            ;; then try xx_YY.dat (without stripping country code).
+            ;; then try xx_YY.dat (without stripping country code),
+            ;; then try xx-alt.dat, for de-alt etc.
             (dolist (tmp-regexp (list "^[[:alpha:]]+"
-                                      "^[[:alpha:]_]+"))
+                                      "^[[:alpha:]_]+"
+                                       "^[[:alpha:]]+-\\(alt\\|old\\)"))
               (let ((fullpath
                      (concat tmp-path "/"
                              (and (string-match tmp-regexp dict-name)
@@ -1117,7 +1115,7 @@ of `ispell-dictionary-base-alist' elements."
                'utf-8)))))
 
 (defun ispell-aspell-add-aliases (alist)
-  "Find aspell's dictionary aliases and add them to dictionary ALIST.
+  "Find Aspell's dictionary aliases and add them to dictionary ALIST.
 Return the new dictionary alist."
   (let ((aliases
          (file-expand-wildcards
@@ -1148,20 +1146,20 @@ Return the new dictionary alist."
 ;; Make ispell.el work better with hunspell.
 
 (defvar ispell-hunspell-dict-paths-alist nil
-  "Alist of parsed hunspell dicts and associated affix files.
+  "Alist of parsed Hunspell dicts and associated affix files.
 Will be used to parse corresponding .aff file and create associated
 parameters to be inserted into `ispell-hunspell-dictionary-alist'.
 Internal use.")
 
 (defvar ispell-hunspell-dictionary-alist nil
-  "Alist of parsed hunspell dicts and associated parameters.
+  "Alist of parsed Hunspell dicts and associated parameters.
 This alist will initially contain names of found dicts.  Associated
 parameters will be added when dict is used for the first time.
 Internal use.")
 
 (defun ispell-hunspell-fill-dictionary-entry (dict)
-  "Fill `ispell-dictionary-alist' uninitialized entries for `DICT' and aliases.
-Value will be extracted from hunspell affix file and used for
+  "Fill uninitialized entries in `ispell-dictionary-alist' for DICT and aliases.
+Value of those entries will be extracted from Hunspell affix file and used for
 all uninitialized dicts using that affix file."
   (if (cadr (assoc dict ispell-dictionary-alist))
       (message "ispell-hfde: Non void entry for %s. Skipping.\n" dict)
@@ -1176,68 +1174,105 @@ all uninitialized dicts using that affix file."
              (dict-equiv-value (cadr dict-equiv-alist-entry)))
          (if (or (member dict dict-equiv-alist-entry)
                  (member dict-alias dict-equiv-alist-entry))
-             (dolist ( tmp-dict (list dict-equiv-key dict-equiv-value))
+             (dolist (tmp-dict (list dict-equiv-key dict-equiv-value))
                (if (cadr (assoc tmp-dict ispell-dictionary-alist))
                    (ispell-print-if-debug
-                     "ispell-hfde: %s already expanded. Skipping.\n" tmp-dict)
-                 (add-to-list 'use-for-dicts tmp-dict))))))
+                     "ispell-hfde: %s already expanded; skipping.\n" tmp-dict)
+                 (cl-pushnew tmp-dict use-for-dicts :test #'equal))))))
       (ispell-print-if-debug
-       "ispell-hfde: Filling %s entry. Use for %s.\n" dict use-for-dicts)
+       "ispell-hfde: Filling %s entry.  Use for %s.\n" dict use-for-dicts)
       ;; The final loop.
       (dolist (entry ispell-dictionary-alist)
-       (if (member (car entry) use-for-dicts)
-           (add-to-list 'newlist
-                        (append (list (car entry)) dict-args-cdr))
-         (add-to-list 'newlist entry)))
+       (cl-pushnew (if (member (car entry) use-for-dicts)
+                        (cons (car entry) dict-args-cdr)
+                      entry)
+                    newlist :test #'equal))
       (setq ispell-dictionary-alist newlist))))
 
 (defun ispell-parse-hunspell-affix-file (dict-key)
-  "Parse hunspell affix file to extract parameters for `DICT-KEY'.
-Return a list in `ispell-dictionary-alist' format."
-  (let ((affix-file (cadr (assoc dict-key ispell-hunspell-dict-paths-alist))))
-    (unless affix-file
-      (error "ispell-phaf: No matching entry for %s.\n" dict-key))
-    (if (not (file-exists-p affix-file))
-       (error "ispell-phaf: File \"%s\" not found.\n" affix-file))
-    (let ((dict-name (file-name-sans-extension
-                      (file-name-nondirectory affix-file)))
-          otherchars-string otherchars-list)
-      (with-temp-buffer
-        (insert-file-contents affix-file)
-        (setq otherchars-string
-              (save-excursion
-                (goto-char (point-min))
-                (if (search-forward-regexp "^WORDCHARS +" nil t )
-                    (buffer-substring (point)
-                                      (progn (end-of-line) (point))))))
-        ;; Remove trailing whitespace and extra stuff.  Make list if
-        ;; non-nil.
-        (setq otherchars-list
-              (if otherchars-string
-                  (split-string
-                   (if (string-match " +.*$" otherchars-string)
-                       (replace-match "" nil nil otherchars-string)
-                     otherchars-string)
-                   "" t)))
-
-        ;; Fill dict entry
-        (list dict-key
-              "[[:alpha:]]"
-              "[^[:alpha:]]"
-              (if otherchars-list
-                  (regexp-opt otherchars-list)
-                "")
-              t                   ; many-otherchars-p: We can't tell, set to t.
-              (list "-d" dict-name)
-              nil              ; extended-char-mode: not supported by hunspell!
-              'utf-8)))))
+  "Parse Hunspell affix file to extract parameters for DICT-KEY.
+Return a list in `ispell-dictionary-alist' format.
+
+DICT_KEY can be in the \"DICT1,DICT2,DICT3\" format, to invoke Hunspell
+with a list of dictionaries.  The first dictionary in the list must have
+a corresponding .aff affix file; the rest are allowed to have no affix
+files, and will then use the affix file of the preceding dictionary that
+did."
+  (let ((dict-list (split-string dict-key "," t))
+        (first-p t)
+        (dict-arg "")
+        otherchars-list)
+    (dolist (dict-key dict-list)
+      (let ((affix-file
+             (cadr (assoc dict-key ispell-hunspell-dict-paths-alist))))
+        (unless affix-file
+          (error "ispell-phaf: No matching entry for %s in `ispell-hunspell-dict-paths-alist'.\n" dict-key))
+        (if (and first-p (not (file-exists-p affix-file)))
+            (error "ispell-phaf: File \"%s\" not found.\n" affix-file))
+        (and first-p (setq first-p nil))
+        (let ((dict-name (file-name-sans-extension
+                          (file-name-nondirectory affix-file)))
+              otherchars-string)
+          (with-temp-buffer
+            (insert-file-contents affix-file)
+            (setq otherchars-string
+                  (save-excursion
+                    (goto-char (point-min))
+                    (if (search-forward-regexp "^WORDCHARS +" nil t )
+                        (buffer-substring (point)
+                                          (progn (end-of-line) (point))))))
+            ;; Remove trailing whitespace and extra stuff.  Make list
+            ;; if non-nil.
+            (if otherchars-string
+                (let* ((otherchars-string
+                        ;; Remove trailing junk.
+                        (substring otherchars-string
+                                   0 (string-match " +" otherchars-string)))
+                       (chars-list (append otherchars-string nil)))
+                  (setq chars-list (delq ?\  chars-list))
+                  (dolist (ch chars-list)
+                    (cl-pushnew ch otherchars-list :test #'equal)))))
+          ;; Cons the argument for the -d switch.
+          (setq dict-arg (concat dict-arg
+                                 (if (> (length dict-arg) 0) ",")
+                                 dict-name)))))
+
+    ;; Fill dict entry
+    (list dict-key
+          "[[:alpha:]]"
+          "[^[:alpha:]]"
+          (if otherchars-list
+              (regexp-opt (mapcar #'char-to-string otherchars-list))
+            "")
+          t                   ; many-otherchars-p: We can't tell, set to t.
+          (list "-d" dict-arg)
+          nil              ; extended-char-mode: not supported by hunspell!
+          'utf-8)))
+
+(defun ispell-hunspell-add-multi-dic (dict)
+  "Add DICT of the form \"DICT1,DICT2,...\" to `ispell-dictionary-alist'.
+
+Invoke this command before you want to start Hunspell for the first time
+with a particular combination of dictionaries.  The first dictionary
+in the list must have an affix file where Hunspell affix files are kept."
+  (interactive "sMulti-dictionary combination: ")
+  ;; Make sure the first dictionary in the list is known to us.
+  (let ((first-dict (car (split-string dict "," t))))
+    (unless ispell-hunspell-dictionary-alist
+      (ispell-find-hunspell-dictionaries)
+      (setq ispell-dictionary-alist ispell-hunspell-dictionary-alist))
+    (or (assoc first-dict ispell-local-dictionary-alist)
+        (assoc first-dict ispell-dictionary-alist)
+        (error "Unknown dictionary: %s" first-dict)))
+  (cl-pushnew (list dict '()) ispell-dictionary-alist :test #'equal)
+  (ispell-hunspell-fill-dictionary-entry dict))
 
 (defun ispell-find-hunspell-dictionaries ()
-  "Look for installed hunspell dictionaries.
+  "Look for installed Hunspell dictionaries.
 Will initialize `ispell-hunspell-dictionary-alist' and
 `ispell-hunspell-dictionary-alist' after values found
 and remove `ispell-dicts-name2locale-equivs-alist'
-entries if a specific dict was found."
+entries if a specific dictionary was found."
   (let ((hunspell-found-dicts
         (split-string
          (with-temp-buffer
@@ -1258,7 +1293,7 @@ entries if a specific dict was found."
        (if (string-match "\\.aff$" dict)
            ;; Found default dictionary
            (if hunspell-default-dict
-               (error "ispell-fhd: Default dict already defined as %s. Not using %s.\n"
+               (error "ispell-fhd: Default dict already defined as %s.  Not using %s.\n"
                       hunspell-default-dict dict)
              (setq affix-file dict)
              (setq hunspell-default-dict (list basename affix-file)))
@@ -1269,8 +1304,8 @@ entries if a specific dict was found."
                (ispell-print-if-debug
                  "++ ispell-fhd: dict-entry:%s name:%s basename:%s affix-file:%s\n"
                  dict full-name basename affix-file)
-               (add-to-list 'ispell-hunspell-dict-paths-alist
-                            (list basename affix-file)))
+               (cl-pushnew (list basename affix-file)
+                            ispell-hunspell-dict-paths-alist :test #'equal))
            (ispell-print-if-debug
              "-- ispell-fhd: Skipping entry: %s\n" dict)))))
     ;; Remove entry from aliases alist if explicit dict was found.
@@ -1278,9 +1313,9 @@ entries if a specific dict was found."
       (dolist (dict ispell-dicts-name2locale-equivs-alist)
        (if (assoc (car dict) ispell-hunspell-dict-paths-alist)
            (ispell-print-if-debug
-             "-- ispell-fhd: Excluding %s alias. Standalone dict found.\n"
+             "-- ispell-fhd: Excluding %s alias.  Standalone dict found.\n"
              (car dict))
-         (add-to-list 'newlist dict)))
+         (cl-pushnew dict newlist :test #'equal)))
       (setq ispell-dicts-name2locale-equivs-alist newlist))
     ;; Add known hunspell aliases
     (dolist (dict-equiv ispell-dicts-name2locale-equivs-alist)
@@ -1298,22 +1333,20 @@ entries if a specific dict was found."
                                            ispell-hunspell-dict-paths-alist))))
              (ispell-print-if-debug "++ ispell-fhd: Adding alias %s -> %s.\n"
                                      dict-equiv-key affix-file)
-             (add-to-list
-              'ispell-hunspell-dict-paths-alist
-              (list dict-equiv-key affix-file))))))
+             (cl-pushnew (list dict-equiv-key affix-file)
+                          ispell-hunspell-dict-paths-alist :test #'equal)))))
     ;; Parse and set values for default dictionary.
     (setq hunspell-default-dict (car hunspell-default-dict))
     (setq hunspell-default-dict-entry
          (ispell-parse-hunspell-affix-file hunspell-default-dict))
     ;; Create an alist of found dicts with only names, except for default dict.
     (setq ispell-hunspell-dictionary-alist
-         (list (append (list nil) (cdr hunspell-default-dict-entry))))
-    (dolist (dict (mapcar 'car ispell-hunspell-dict-paths-alist))
-      (if (string= dict hunspell-default-dict)
-         (add-to-list 'ispell-hunspell-dictionary-alist
-                      hunspell-default-dict-entry)
-       (add-to-list 'ispell-hunspell-dictionary-alist
-                    (list dict))))))
+         (list (cons nil (cdr hunspell-default-dict-entry))))
+    (dolist (dict (mapcar #'car ispell-hunspell-dict-paths-alist))
+      (cl-pushnew (if (string= dict hunspell-default-dict)
+                      hunspell-default-dict-entry
+                    (list dict))
+                  ispell-hunspell-dictionary-alist :test #'equal))))
 
 ;; Set params according to the selected spellchecker
 
@@ -1399,22 +1432,22 @@ aspell is used along with Emacs).")
                        (setq ispell-args
                              (nconc ispell-args (list "-d" dict-equiv)))
                      (message
-                      "ispell-set-spellchecker-params: Missing hunspell equiv for \"%s\". Skipping."
+                      "ispell-set-spellchecker-params: Missing Hunspell equiv for \"%s\". Skipping."
                       dict-name)
                      (setq skip-dict t)))
 
                (unless skip-dict
-                 (add-to-list 'tmp-dicts-alist
-                              (list
-                               dict-name      ; dict name
-                               (nth 1 adict)  ; casechars
-                               (nth 2 adict)  ; not-casechars
-                               (nth 3 adict)  ; otherchars
-                               (nth 4 adict)  ; many-otherchars-p
-                               ispell-args    ; ispell-args
-                               (nth 6 adict)  ; extended-character-mode
-                               (nth 7 adict)  ; dict encoding
-                               ))))
+                 (cl-pushnew (list
+                               dict-name      ; dict name
+                               (nth 1 adict)  ; casechars
+                               (nth 2 adict)  ; not-casechars
+                               (nth 3 adict)  ; otherchars
+                               (nth 4 adict)  ; many-otherchars-p
+                               ispell-args    ; ispell-args
+                               (nth 6 adict)  ; extended-character-mode
+                               (nth 7 adict)  ; dict encoding
+                               )
+                              tmp-dicts-alist :test #'equal)))
              (setq ispell-dictionary-base-alist tmp-dicts-alist))))
 
       (run-hooks 'ispell-initialize-spellchecker-hook)
@@ -1424,7 +1457,7 @@ aspell is used along with Emacs).")
                            ispell-base-dicts-override-alist
                            ispell-dictionary-base-alist))
        (unless (assoc (car dict) all-dicts-alist)
-         (add-to-list 'all-dicts-alist dict)))
+         (push dict all-dicts-alist)))
       (setq ispell-dictionary-alist all-dicts-alist))
 
     ;; If Emacs flavor supports [:alpha:] use it for global dicts.  If
@@ -1434,20 +1467,20 @@ aspell is used along with Emacs).")
     (if ispell-emacs-alpha-regexp
        (let (tmp-dicts-alist)
          (dolist (adict ispell-dictionary-alist)
-           (if (cadr adict) ;; Do not touch hunspell uninitialized entries
-               (add-to-list 'tmp-dicts-alist
-                            (list
-                             (nth 0 adict)  ; dict name
-                             "[[:alpha:]]"  ; casechars
-                             "[^[:alpha:]]" ; not-casechars
-                             (nth 3 adict)  ; otherchars
-                             (nth 4 adict)  ; many-otherchars-p
-                             (nth 5 adict)  ; ispell-args
-                             (nth 6 adict)  ; extended-character-mode
-                             (if ispell-encoding8-command
-                                 'utf-8
-                               (nth 7 adict))))
-             (add-to-list 'tmp-dicts-alist adict)))
+           (cl-pushnew (if (cadr adict) ;; Do not touch hunspell uninitialized entries
+                            (list
+                             (nth 0 adict)   ; dict name
+                             "[[:alpha:]]"   ; casechars
+                             "[^[:alpha:]]"  ; not-casechars
+                             (nth 3 adict)   ; otherchars
+                             (nth 4 adict)   ; many-otherchars-p
+                             (nth 5 adict)   ; ispell-args
+                             (nth 6 adict)   ; extended-character-mode
+                             (if ispell-encoding8-command
+                                 'utf-8
+                               (nth 7 adict)))
+                          adict)
+                        tmp-dicts-alist :test #'equal))
          (setq ispell-dictionary-alist tmp-dicts-alist)))))
 
 (defun ispell-valid-dictionary-list ()
@@ -1617,12 +1650,12 @@ The variable `ispell-library-directory' defines their location."
 
 (defvar ispell-current-dictionary nil
   "The name of the current dictionary, or nil for the default.
-This is passed to the ispell process using the `-d' switch and is
+This is passed to the Ispell process using the `-d' switch and is
 used as key in `ispell-local-dictionary-alist' and `ispell-dictionary-alist'.")
 
 (defvar ispell-current-personal-dictionary nil
   "The name of the current personal dictionary, or nil for the default.
-This is passed to the ispell process using the `-p' switch.")
+This is passed to the Ispell process using the `-p' switch.")
 
 (defun ispell-decode-string (str)
   "Decodes multibyte character strings.
@@ -1743,6 +1776,51 @@ Extended character mode can be changed for this buffer by placing
 a `~' followed by an extended-character mode -- such as `~.tex'.
 The last occurring definition in the buffer will be used.")
 
+(defun ispell--\\w-filter (char)
+  "Return CHAR in a string when CHAR doesn't have \"word\" syntax,
+nil otherwise.  CHAR must be a character."
+  (let ((str (string char)))
+    (and
+     (not (string-match "\\w" str))
+     str)))
+
+(defun ispell--make-\\w-expression (chars)
+  "Make a regular expression like \"\\(\\w\\|[-_]\\)\".
+This (parenthesized) expression matches either a character of
+\"word\" syntax or one in CHARS.
+
+CHARS is a string of characters.  A member of CHARS is omitted
+from the expression if it already has word syntax.  (Be careful
+about special characters such as ?\\, ?^, ?], and ?- in CHARS.)
+If after this filtering there are no chars left, or only one, a
+special form of the expression is generated."
+  (let ((filtered
+        (mapconcat #'ispell--\\w-filter chars "")))
+    (concat
+     "\\(\\w"
+     (cond
+      ((equal filtered "")
+       "\\)")
+      ((eq (length filtered) 1)
+       (concat "\\|" filtered "\\)"))
+      (t
+       (concat "\\|[" filtered "]\\)"))))))
+
+(defun ispell--make-filename-or-URL-re ()
+  "Construct a regexp to match some file names or URLs or email addresses.
+The expression is crafted to match as great a variety of these
+objects as practicable, without too many false matches happening."
+  (concat ;"\\(--+\\|_+\\|"
+          "\\(/\\w\\|\\("
+          (ispell--make-\\w-expression "-_")
+          "+[.:@]\\)\\)"
+          (ispell--make-\\w-expression "-_")
+          "*\\([.:/@]+"
+          (ispell--make-\\w-expression "-_~=?&")
+          "+\\)+"
+          ;"\\)"
+          ))
+
 ;;;###autoload
 (defvar ispell-skip-region-alist
   `((ispell-words-keyword         forward-line)
@@ -1759,7 +1837,7 @@ The last occurring definition in the buffer will be used.")
     ;; Matches e-mail addresses, file names, http addresses, etc.  The
     ;; `-+' `_+' patterns are necessary for performance reasons when
     ;; `-' or `_' part of word syntax.
-    (,(purecopy "\\(--+\\|_+\\|\\(/\\w\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)"))
+;    (,(purecopy "\\(--+\\|_+\\|\\(/\\w\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)"))
     ;; above checks /.\w sequences
     ;;("\\(--+\\|\\(/\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)")
     ;; This is a pretty complex regexp.  It can be simplified to the following:
@@ -1791,6 +1869,7 @@ Valid forms include:
      ("\\\\add\\(tocontents\\|vspace\\)" ispell-tex-arg-end)
      ("\\\\\\([aA]lph\\|arabic\\)"      ispell-tex-arg-end)
      ;;("\\\\author"                    ispell-tex-arg-end)
+     ("\\\\cref"                        ispell-tex-arg-end)
      ("\\\\bibliographystyle"           ispell-tex-arg-end)
      ("\\\\makebox"                     ispell-tex-arg-end 0)
      ("\\\\e?psfig"                     ispell-tex-arg-end)
@@ -1857,7 +1936,7 @@ Currently the only other valid parser is `tex'.
 
 You can set this variable in hooks in your init file -- eg:
 
-\(add-hook 'tex-mode-hook (lambda () (setq ispell-parser 'tex)))")
+\(add-hook \\='tex-mode-hook (lambda () (setq ispell-parser \\='tex)))")
 
 (defvar ispell-region-end (make-marker)
   "Marker that allows spelling continuations.")
@@ -1875,9 +1954,9 @@ You can set this variable in hooks in your init file -- eg:
 
 
 (defun ispell-accept-output (&optional timeout-secs timeout-msecs)
-  "Wait for output from ispell process, or TIMEOUT-SECS and TIMEOUT-MSECS.
+  "Wait for output from Ispell process, or TIMEOUT-SECS and TIMEOUT-MSECS.
 If asynchronous subprocesses are not supported, call function `ispell-filter'
-and pass it the output of the last ispell invocation."
+and pass it the output of the last Ispell invocation."
   (if ispell-async-processp
       (accept-process-output ispell-process timeout-secs timeout-msecs)
     (if (null ispell-process)
@@ -1894,8 +1973,8 @@ and pass it the output of the last ispell invocation."
            (erase-buffer)))))))
 
 (defun ispell-send-replacement (misspelled replacement)
-  "Notify aspell that MISSPELLED should be spelled REPLACEMENT.
-This allows it to improve the suggestion list based on actual misspellings."
+  "Notify Aspell that MISSPELLED should be spelled REPLACEMENT.
+This allows improving the suggestion list based on actual misspellings."
   (and ispell-really-aspell
        (ispell-send-string (concat "$$ra " misspelled "," replacement "\n"))))
 
@@ -1985,7 +2064,7 @@ in a window allowing you to choose one.
 
 If optional argument FOLLOWING is non-nil or if `ispell-following-word'
 is non-nil when called interactively, then the following word
-\(rather than preceding\) is checked when the cursor is not over a word.
+\(rather than preceding) is checked when the cursor is not over a word.
 When the optional argument QUIETLY is non-nil or `ispell-quietly' is non-nil
 when called interactively, non-corrective messages are suppressed.
 
@@ -2005,7 +2084,7 @@ Return values:
 nil           word is correct or spelling is accepted.
 0             word is inserted into buffer-local definitions.
 \"word\"        word corrected from word list.
-\(\"word\" arg\)  word is hand entered.
+\(\"word\" arg)  word is hand entered.
 quit          spell session exited."
   (interactive (list ispell-following-word ispell-quietly current-prefix-arg t))
   (cond
@@ -2123,7 +2202,7 @@ quit          spell session exited."
   "Return the word for spell-checking according to ispell syntax.
 If optional argument FOLLOWING is non-nil or if `ispell-following-word'
 is non-nil when called interactively, then the following word
-\(rather than preceding\) is checked when the cursor is not over a word.
+\(rather than preceding) is checked when the cursor is not over a word.
 Optional second argument contains otherchars that can be included in word
 many times (see the doc string of `ispell-dictionary-alist' for details
 about otherchars).
@@ -2209,6 +2288,11 @@ If so, ask if it needs to be saved."
   (setq ispell-pdict-modified-p nil))
 
 
+(defvar ispell-update-post-hook nil
+  "A normal hook invoked from the ispell command loop.
+It is called once per iteration, before displaying a prompt to
+the user.")
+
 (defun ispell-command-loop (miss guess word start end)
   "Display possible corrections from list MISS.
 GUESS lists possibly valid affix construction of WORD.
@@ -2258,7 +2342,8 @@ Global `ispell-quit' set to start location to continue spell session."
                (insert "\n\t"))
              (insert (car guess) "    ")
              (setq guess (cdr guess)))
-           (insert "\nUse option `i' to accept this spelling and put it in your private dictionary.\n")))
+           (insert (substitute-command-keys
+                    "\nUse option `i' to accept this spelling and put it in your private dictionary.\n"))))
       (while choices
        (when (> (+ 7 (current-column)
                    (length (car choices))
@@ -2275,8 +2360,10 @@ Global `ispell-quit' set to start location to continue spell session."
              count (ispell-int-char (1+ count))))
       (setq count (ispell-int-char (- count ?0 skipped))))
 
+    (run-hooks 'ispell-update-post-hook)
+
     ;; ensure word is visible
-    (if (not (pos-visible-in-window-p end))
+    (if (not (pos-visible-in-window-group-p end))
        (sit-for 0))
 
     ;; Display choices for misspelled word.
@@ -2335,7 +2422,8 @@ Global `ispell-quit' set to start location to continue spell session."
                    nil)
                   ((or (= char ?a) (= char ?A)) ; accept word without insert
                    (ispell-send-string (concat "@" word "\n"))
-                   (add-to-list 'ispell-buffer-session-localwords word)
+                   (cl-pushnew word ispell-buffer-session-localwords
+                                :test #'equal)
                    (and (fboundp 'flyspell-unhighlight-at)
                         (flyspell-unhighlight-at start))
                    (or ispell-buffer-local-name ; session localwords might conflict
@@ -2668,7 +2756,7 @@ if defined."
 ;;   This is the case when a process dies or fails. The default behavior
 ;;   in this case treats the next input received as fresh input.
 
-(defun ispell-filter (process output)
+(defun ispell-filter (_process output)
   "Output filter function for ispell, grep, and look."
   (let ((start 0)
        (continue t)
@@ -2769,6 +2857,7 @@ The variable `ispell-highlight-face' selects the face to use for highlighting."
                  (regexp-quote (buffer-substring-no-properties start end))
                  "\\b"))
                (isearch-regexp t)
+               (isearch-regexp-function nil)
                (isearch-case-fold-search nil)
                (isearch-forward t)
                (isearch-other-end start)
@@ -2804,13 +2893,20 @@ Also position fit window to BUFFER and select it."
                     (prog1
                         (condition-case nil
                             (split-window
-                             nil (- ispell-choices-win-default-height) 'above)
+                              ;; Chose the last of a window group, since
+                              ;; otherwise, the lowering of another window's
+                              ;; TL corner would cause the logical order of
+                              ;; the windows to be changed.
+                             (car (last (selected-window-group)))
+                              (- ispell-choices-win-default-height) 'above)
                           (error nil))
                       (modify-frame-parameters frame '((unsplittable . t))))))
              (and (not unsplittable)
                   (condition-case nil
                       (split-window
-                       nil (- ispell-choices-win-default-height) 'above)
+                        ;; See comment above.
+                       (car (last (selected-window-group)))
+                        (- ispell-choices-win-default-height) 'above)
                     (error nil)))
              (display-buffer buffer))))
     (if (not window)
@@ -2940,14 +3036,13 @@ Keeps argument list for future Ispell invocations for no async support."
       (ispell-send-string "\032\n")    ; so Ispell prints version and exits
       t)))
 
-
 (defun ispell-init-process ()
   "Check status of Ispell process and start if necessary."
   (let* (;; Basename of dictionary used by the spell-checker
         (dict-bname (or (car (cdr (member "-d" (ispell-get-ispell-args))))
                         ispell-current-dictionary))
         ;; The directory where process was started.
-        (current-ispell-directory default-directory)
+        (current-ispell-directory default-directory) ;FIXME: Unused?
         ;; The default directory for the process.
         ;; Use "~/" as default-directory unless using Ispell with per-dir
         ;; personal dictionaries and not in a minibuffer under XEmacs
@@ -2986,10 +3081,14 @@ Keeps argument list for future Ispell invocations for no async support."
               (or ispell-local-dictionary ispell-dictionary "default"))
       (sit-for 0)
       (setq ispell-library-directory (ispell-check-version)
+            ;; Assign a non-nil value to ispell-process-directory
+            ;; before calling ispell-start-process, since that
+            ;; function needs it to set default-directory when
+            ;; ispell-async-processp is nil.
+           ispell-process-directory default-directory
            ispell-process (ispell-start-process)
            ispell-filter nil
-           ispell-filter-continue nil
-           ispell-process-directory default-directory)
+           ispell-filter-continue nil)
 
       (unless (equal ispell-process-directory (expand-file-name "~/"))
        ;; At this point, `ispell-process-directory' will be "~/" unless using
@@ -3015,7 +3114,12 @@ Keeps argument list for future Ispell invocations for no async support."
       (if (and (or (featurep 'xemacs)
                   (and (boundp 'enable-multibyte-characters)
                        enable-multibyte-characters))
-              (fboundp 'set-process-coding-system))
+              (fboundp 'set-process-coding-system)
+               ;; Evidently, some people use the synchronous mode even
+               ;; when async subprocesses are supported, in which case
+               ;; set-process-coding-system is bound, but
+               ;; ispell-process is not a process object.
+               ispell-async-processp)
          (set-process-coding-system ispell-process (ispell-get-coding-system)
                                     (ispell-get-coding-system)))
       ;; Get version ID line
@@ -3041,7 +3145,7 @@ Keeps argument list for future Ispell invocations for no async support."
             ;; Otherwise we get cool errors like "Can't open ".
             (sleep-for 1)
             (ispell-accept-output 3)
-            (error "%s" (mapconcat 'identity ispell-filter "\n"))))
+            (error "%s" (mapconcat #'identity ispell-filter "\n"))))
       (setq ispell-filter nil)         ; Discard version ID line
       (let ((extended-char-mode (ispell-get-extended-character-mode)))
        (if extended-char-mode          ; ~ extended character mode
@@ -3097,7 +3201,7 @@ By just answering RET you can find out what the current dictionary is."
    (list (completing-read
          "Use new dictionary (RET for current, SPC to complete): "
          (and (fboundp 'ispell-valid-dictionary-list)
-              (mapcar 'list (ispell-valid-dictionary-list)))
+              (mapcar #'list (ispell-valid-dictionary-list)))
          nil t)
         current-prefix-arg))
   (ispell-set-spellchecker-params) ; Initialize variables and dicts alists
@@ -3303,7 +3407,7 @@ ispell-region: Search for first region to skip after (ispell-begin-skip-region-r
 Includes `ispell-skip-region-alist' plus tex, tib, html, and comment keys.
 Must be called after `ispell-buffer-local-parsing' due to dependence on mode."
   (mapconcat
-   'identity
+   #'identity
    (delq nil
          (list
           ;; messages
@@ -3324,7 +3428,8 @@ Must be called after `ispell-buffer-local-parsing' due to dependence on mode."
               (if (string= "" comment-end) "^" (regexp-quote comment-end)))
           (if (and (null ispell-check-comments) comment-start)
               (regexp-quote comment-start))
-          (ispell-begin-skip-region ispell-skip-region-alist)))
+          (ispell-begin-skip-region ispell-skip-region-alist)
+          (ispell--make-filename-or-URL-re)))
    "\\|"))
 
 
@@ -3363,6 +3468,8 @@ Manual checking must include comments and tib references.
 The list is of the form described by variable `ispell-skip-region-alist'.
 Must be called after `ispell-buffer-local-parsing' due to dependence on mode."
   (let ((skip-alist ispell-skip-region-alist))
+    (setq skip-alist (append (list (list (ispell--make-filename-or-URL-re)))
+                             skip-alist))
     ;; only additional explicit region definition is tex.
     (if (eq ispell-parser 'tex)
        (setq case-fold-search nil
@@ -3391,7 +3498,7 @@ Must be called after `ispell-buffer-local-parsing' due to dependence on mode."
 
 (defun ispell-ignore-fcc (start end)
   "Delete the Fcc: message header when large attachments are included.
-Return value `nil' if file with large attachments is saved.
+Return value nil if file with large attachments is saved.
 This can be used to avoid multiple questions for multiple large attachments.
 Returns point to starting location afterwards."
   (let ((result t))
@@ -3691,7 +3798,7 @@ Returns the sum SHIFT due to changes in word replacements."
 ;;;###autoload
 (defun ispell-buffer-with-debug (&optional append)
   "`ispell-buffer' with some output sent to `ispell-debug-buffer' buffer.
-Use APPEND to append the info to previous buffer if exists."
+If APPEND is non-n il, append the info to previous buffer if exists."
   (interactive)
   (let ((ispell-debug-buffer (ispell-create-debug-buffer append)))
     (ispell-buffer)))
@@ -3728,8 +3835,8 @@ Use APPEND to append the info to previous buffer if exists."
 
 ;;;###autoload
 (defun ispell-complete-word (&optional interior-frag)
-  "Try to complete the word before or under point.
-If optional INTERIOR-FRAG is non-nil then the word may be a character
+  "Try to complete the word before or at point.
+If optional INTERIOR-FRAG is non-nil, then the word may be a character
 sequence inside of a word.
 
 Standard ispell choices are then available."
@@ -3757,7 +3864,7 @@ Standard ispell choices are then available."
           (setq case-fold-search nil)  ; Try and respect case of word.
           (cond
            ((string-equal (upcase word) word)
-            (setq possibilities (mapcar 'upcase possibilities)))
+            (setq possibilities (mapcar #'upcase possibilities)))
            ((eq (upcase (aref word 0)) (aref word 0))
              (setq possibilities (mapcar (function
                                           (lambda (pos)
@@ -3835,7 +3942,7 @@ typing SPC or RET warns you if the previous word is incorrectly
 spelled.
 
 All the buffer-local variables and dictionaries are ignored.  To
-read them into the running ispell process, type \\[ispell-word]
+read them into the running Ispell process, type \\[ispell-word]
 SPC.
 
 For spell-checking \"on the fly\", not just after typing SPC or
@@ -3889,7 +3996,7 @@ Otherwise, it must be a function which is called to get the limit.")
 
 (defun ispell-mime-multipartp (&optional limit)
   "Return multipart message start boundary or nil if none."
-  ;; caller must ensure `case-fold-search' is set to `t'
+  ;; caller must ensure `case-fold-search' is set to t
   (and
    (re-search-forward
     "Content-Type: *multipart/\\([^ \t\n]*;[ \t]*[\n]?[ \t]*\\)+boundary="
@@ -3991,14 +4098,14 @@ The `X' command aborts sending the message so that you can edit the buffer.
 
 To spell-check whenever a message is sent, include the appropriate lines
 in your init file:
-   (add-hook 'message-send-hook 'ispell-message)  ;; GNUS 5
-   (add-hook 'news-inews-hook 'ispell-message)    ;; GNUS 4
-   (add-hook 'mail-send-hook  'ispell-message)
-   (add-hook 'mh-before-send-letter-hook 'ispell-message)
+   (add-hook \\='message-send-hook #\\='ispell-message)  ;; GNUS 5
+   (add-hook \\='news-inews-hook #\\='ispell-message)    ;; GNUS 4
+   (add-hook \\='mail-send-hook  #\\='ispell-message)
+   (add-hook \\='mh-before-send-letter-hook #\\='ispell-message)
 
 You can bind this to the key C-c i in GNUS or mail by adding to
 `news-reply-mode-hook' or `mail-mode-hook' the following lambda expression:
-   (function (lambda () (local-set-key \"\\C-ci\" 'ispell-message)))"
+   (function (lambda () (local-set-key \"\\C-ci\" \\='ispell-message)))"
   (interactive)
   (save-excursion
     (goto-char (point-min))
@@ -4056,9 +4163,10 @@ You can bind this to the key C-c i in GNUS or mail by adding to
                      (ispell-non-empty-string vm-included-text-prefix)))
             (t default-prefix)))
           (ispell-skip-region-alist
-           (cons (list (concat "^\\(" cite-regexp "\\)")
-                       (function forward-line))
-                 ispell-skip-region-alist))
+           (cons (list (ispell--make-filename-or-URL-re))
+                  (cons (list (concat "^\\(" cite-regexp "\\)")
+                              (function forward-line))
+                        ispell-skip-region-alist)))
           (old-case-fold-search case-fold-search)
           (dictionary-alist ispell-message-dictionary-alist)
           (ispell-checking-message t))
@@ -4315,6 +4423,7 @@ Both should not be used to define a buffer-local dictionary."
                          (insert comment-end)))))
              (insert (concat " " word))))))))
 
+;;FIXME: Use `user-error' instead!
 (add-to-list 'debug-ignored-errors "^No word found to check!$")
 
 (provide 'ispell)