]> code.delx.au - gnu-emacs/blobdiff - lisp/complete.el
Fixes: debbugs:6594
[gnu-emacs] / lisp / complete.el
index cde669a943fcd886fcd295ea895b3d898ad0599d..0f8e52630f6324d20b5d9a2a267d81d00b2d59e8 100644 (file)
@@ -1,7 +1,7 @@
 ;;; complete.el --- partial completion mechanism plus other goodies
 
-;; Copyright (C) 1990, 1991, 1992, 1993, 1999, 2000, 2002, 2003, 2004,
-;;   2005 Free Software Foundation, Inc.
+;; Copyright (C) 1990, 1991, 1992, 1993, 1999, 2000, 2001, 2002, 2003,
+;;   2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
 ;; Author: Dave Gillespie <daveg@synaptics.com>
 ;; Keywords: abbrev convenience
@@ -9,10 +9,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,9 +20,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -94,7 +92,7 @@
   :group 'convenience)
 
 (defcustom PC-first-char 'find-file
-  "*Control how the first character of a string is to be interpreted.
+  "Control how the first character of a string is to be interpreted.
 If nil, the first character of a string is not taken literally if it is a word
 delimiter, so that \".e\" matches \"*.e*\".
 If t, the first character of a string is always taken literally even if it is a
@@ -107,13 +105,13 @@ completion."
   :group 'partial-completion)
 
 (defcustom PC-meta-flag t
-  "*If non-nil, TAB means PC completion and M-TAB means normal completion.
+  "If non-nil, TAB means PC completion and M-TAB means normal completion.
 Otherwise, TAB means normal completion and M-TAB means Partial Completion."
   :type 'boolean
   :group 'partial-completion)
 
 (defcustom PC-word-delimiters "-_. "
-  "*A string of characters treated as word delimiters for completion.
+  "A string of characters treated as word delimiters for completion.
 Some arcane rules:
 If `]' is in this string, it must come first.
 If `^' is in this string, it must not come first.
@@ -124,13 +122,13 @@ expression (not containing character ranges like `a-z')."
   :group 'partial-completion)
 
 (defcustom PC-include-file-path '("/usr/include" "/usr/local/include")
-  "*A list of directories in which to look for include files.
+  "A list of directories in which to look for include files.
 If nil, means use the colon-separated path in the variable $INCPATH instead."
   :type '(repeat directory)
   :group 'partial-completion)
 
 (defcustom PC-disable-includes nil
-  "*If non-nil, include-file support in \\[find-file] is disabled."
+  "If non-nil, include-file support in \\[find-file] is disabled."
   :type 'boolean
   :group 'partial-completion)
 
@@ -141,26 +139,25 @@ If nil, means use the colon-separated path in the variable $INCPATH instead."
   "A list of the environment variable names and values.")
 
 \f
-(defvar PC-old-read-file-name-internal nil)
-
 (defun PC-bindings (bind)
   (let ((completion-map minibuffer-local-completion-map)
        (must-match-map minibuffer-local-must-match-map))
     (cond ((not bind)
           ;; These bindings are the default bindings.  It would be better to
           ;; restore the previous bindings.
+          (define-key read-expression-map "\e\t" 'lisp-complete-symbol)
+
           (define-key completion-map "\t"      'minibuffer-complete)
           (define-key completion-map " "       'minibuffer-complete-word)
           (define-key completion-map "?"       'minibuffer-completion-help)
 
-          (define-key must-match-map "\t"      'minibuffer-complete)
-          (define-key must-match-map " "       'minibuffer-complete-word)
           (define-key must-match-map "\r"      'minibuffer-complete-and-exit)
           (define-key must-match-map "\n"      'minibuffer-complete-and-exit)
-          (define-key must-match-map "?"       'minibuffer-completion-help)
 
-          (define-key global-map "\e\t"        'complete-symbol))
+          (define-key global-map [remap lisp-complete-symbol]  nil))
          (PC-default-bindings
+          (define-key read-expression-map "\e\t" 'PC-lisp-complete-symbol)
+
           (define-key completion-map "\t"      'PC-complete)
           (define-key completion-map " "       'PC-complete-word)
           (define-key completion-map "?"       'PC-completion-help)
@@ -171,19 +168,24 @@ If nil, means use the colon-separated path in the variable $INCPATH instead."
           (define-key completion-map "\e\n"    'PC-force-complete-and-exit)
           (define-key completion-map "\e?"     'PC-completion-help)
 
-          (define-key must-match-map "\t"      'PC-complete)
-          (define-key must-match-map " "       'PC-complete-word)
           (define-key must-match-map "\r"      'PC-complete-and-exit)
           (define-key must-match-map "\n"      'PC-complete-and-exit)
-          (define-key must-match-map "?"       'PC-completion-help)
 
-          (define-key must-match-map "\e\t"    'PC-complete)
-          (define-key must-match-map "\e "     'PC-complete-word)
           (define-key must-match-map "\e\r"    'PC-complete-and-exit)
           (define-key must-match-map "\e\n"    'PC-complete-and-exit)
-          (define-key must-match-map "\e?"     'PC-completion-help)
 
-          (define-key global-map "\e\t"        'PC-lisp-complete-symbol)))))
+          (define-key global-map [remap lisp-complete-symbol]  'PC-lisp-complete-symbol)))))
+
+(defvar PC-do-completion-end nil
+  "Internal variable used by `PC-do-completion'.")
+
+(make-variable-buffer-local 'PC-do-completion-end)
+
+(defvar PC-goto-end nil
+   "Internal variable set in `PC-do-completion', used in
+`choose-completion-string-functions'.")
+
+(make-variable-buffer-local 'PC-goto-end)
 
 ;;;###autoload
 (define-minor-mode partial-completion-mode
@@ -198,7 +200,7 @@ as much as possible and `*' characters are treated likewise in file names.
 For example, M-x p-c-m expands to M-x partial-completion-mode since no other
 command begins with that sequence of characters, and
 \\[find-file] f_b.c TAB might complete to foo_bar.c if that file existed and no
-other file in that directory begin with that sequence of characters.
+other file in that directory begins with that sequence of characters.
 
 Unless `PC-disable-includes' is non-nil, the `<...>' sequence is interpreted
 specially in \\[find-file].  For example,
@@ -218,22 +220,37 @@ second TAB brings up the `*Completions*' buffer."
         (remove-hook 'find-file-not-found-functions 'PC-look-for-include-file))
        ((not PC-disable-includes)
         (add-hook 'find-file-not-found-functions 'PC-look-for-include-file)))
-  ;; ... with some underhand redefining.
-  (cond ((and (not partial-completion-mode)
-             (functionp PC-old-read-file-name-internal))
-        (fset 'read-file-name-internal PC-old-read-file-name-internal))
-       ((and (not PC-disable-includes) (not PC-old-read-file-name-internal))
-        (setq PC-old-read-file-name-internal
-              (symbol-function 'read-file-name-internal))
-        (fset 'read-file-name-internal
-              'PC-read-include-file-name-internal)))
-    (when (and partial-completion-mode (null PC-env-vars-alist))
-      (setq PC-env-vars-alist
-           (mapcar (lambda (string)
-                     (let ((d (string-match "=" string)))
-                       (cons (concat "$" (substring string 0 d))
-                             (and d (substring string (1+ d))))))
-                   process-environment))))
+  ;; Adjust the completion selection in *Completion* buffers to the way
+  ;; we work.  The default minibuffer completion code only completes the
+  ;; text before point and leaves the text after point alone (new in
+  ;; Emacs-22).  In contrast we use the whole text and we even sometimes
+  ;; move point to a place before EOB, to indicate the first position where
+  ;; there's a difference, so when the user uses choose-completion, we have
+  ;; to trick choose-completion into replacing the whole minibuffer text
+  ;; rather than only the text before point.  --Stef
+  (funcall
+   (if partial-completion-mode 'add-hook 'remove-hook)
+   'choose-completion-string-functions
+   (lambda (choice buffer &rest ignored)
+     ;; When completing M-: (lisp- ) with point before the ), it is
+     ;; not appropriate to go to point-max (unlike the filename case).
+     (if (and (not PC-goto-end)
+              (minibufferp buffer))
+         (goto-char (point-max))
+       ;; Need a similar hack for the non-minibuffer-case -- gm.
+       (when PC-do-completion-end
+         (goto-char PC-do-completion-end)
+         (setq PC-do-completion-end nil)))
+     (setq PC-goto-end nil)
+     nil))
+  ;; Build the env-completion and mapping table.
+  (when (and partial-completion-mode (null PC-env-vars-alist))
+    (setq PC-env-vars-alist
+          (mapcar (lambda (string)
+                    (let ((d (string-match "=" string)))
+                      (cons (concat "$" (substring string 0 d))
+                            (and d (substring string (1+ d))))))
+                  process-environment))))
 
 \f
 (defun PC-complete ()
@@ -273,7 +290,7 @@ See `PC-complete' for details.
 This can be bound to other keys, like `-' and `.', if you wish."
   (interactive)
   (if (eq (PC-was-meta-key) PC-meta-flag)
-      (if (eq last-command-char ? )
+      (if (eq last-command-event ? )
          (minibuffer-complete-word)
        (self-insert-command 1))
     (self-insert-command 1)
@@ -309,14 +326,33 @@ See `PC-complete' for details."
     (PC-do-complete-and-exit)))
 
 (defun PC-do-complete-and-exit ()
-  (if (= (point-max) (minibuffer-prompt-end))  ; Duplicate the "bug" that Info-menu relies on...
-      (exit-minibuffer)
+  (cond
+   ((= (point-max) (minibuffer-prompt-end))
+    ;; Duplicate the "bug" that Info-menu relies on...
+    (exit-minibuffer))
+   ((eq minibuffer-completion-confirm 'confirm)
+    (if (or (eq last-command this-command)
+            (test-completion (field-string)
+                             minibuffer-completion-table
+                             minibuffer-completion-predicate))
+        (exit-minibuffer)
+      (PC-temp-minibuffer-message " [Confirm]")))
+   ((eq minibuffer-completion-confirm 'confirm-after-completion)
+    ;; Similar to the above, but only if trying to exit immediately
+    ;; after typing TAB (this catches most minibuffer typos).
+    (if (and (memq last-command minibuffer-confirm-exit-commands)
+            (not (test-completion (field-string)
+                                  minibuffer-completion-table
+                                  minibuffer-completion-predicate)))
+       (PC-temp-minibuffer-message " [Confirm]")
+      (exit-minibuffer)))
+   (t
     (let ((flag (PC-do-completion 'exit)))
       (and flag
           (if (or (eq flag 'complete)
                   (not minibuffer-completion-confirm))
               (exit-minibuffer)
-            (PC-temp-minibuffer-message " [Confirm]"))))))
+            (PC-temp-minibuffer-message " [Confirm]")))))))
 
 
 (defun PC-completion-help ()
@@ -349,21 +385,79 @@ See `PC-complete' for details."
 The function takes no arguments, and typically looks at the value
 of `minibuffer-completion-table' and the minibuffer contents.")
 
-(defun PC-do-completion (&optional mode beg end)
+;; Returns the sequence of non-delimiter characters that follow regexp in string.
+(defun PC-chunk-after (string regexp)
+  (if (not (string-match regexp string))
+      (let ((message "String %s didn't match regexp %s"))
+       (message message string regexp)
+       (error message string regexp)))
+  (let ((result (substring string (match-end 0))))
+    ;; result may contain multiple chunks
+    (if (string-match PC-delim-regex result)
+       (setq result (substring result 0 (match-beginning 0))))
+    result))
+
+(defun test-completion-ignore-case (str table pred)
+  "Like `test-completion', but ignores case when possible."
+  ;; Binding completion-ignore-case to nil ensures, for compatibility with
+  ;; standard completion, that the return value is exactly one of the
+  ;; possibilities.  Do this binding only if pred is nil, out of paranoia;
+  ;; perhaps it is safe even if pred is non-nil.
+  (if pred
+      (test-completion str table pred)
+    (let ((completion-ignore-case nil))
+      (test-completion str table pred))))
+
+;; The following function is an attempt to work around two problems:
+
+;; (1) When complete.el was written, (try-completion "" '(("") (""))) used to
+;; return the value "".  With a change from 2002-07-07 it returns t which caused
+;; `PC-lisp-complete-symbol' to fail with a "Wrong type argument: sequencep, t"
+;; error.  `PC-try-completion' returns STRING in this case.
+
+;; (2) (try-completion "" '((""))) returned t before the above-mentioned change.
+;; Since `PC-chop-word' operates on the return value of `try-completion' this
+;; case might have provoked a similar error as in (1).  `PC-try-completion'
+;; returns "" instead.  I don't know whether this is a real problem though.
+
+;; Since `PC-try-completion' is not a guaranteed to fix these bugs reliably, you
+;; should try to look at the following discussions when you encounter problems:
+;; - emacs-pretest-bug ("Partial Completion" starting 2007-02-23),
+;; - emacs-devel ("[address-of-OP: Partial completion]" starting 2007-02-24),
+;; - emacs-devel ("[address-of-OP: EVAL and mouse selection in *Completions*]"
+;;   starting 2007-03-05).
+(defun PC-try-completion (string alist &optional predicate)
+  "Like `try-completion' but return STRING instead of t."
+  (let ((result (try-completion string alist predicate)))
+    (if (eq result t) string result)))
+
+;; TODO document MODE magic...
+(defun PC-do-completion (&optional mode beg end goto-end)
+  "Internal function to do the work of partial completion.
+Text to be completed lies between BEG and END.  Normally when
+replacing text in the minibuffer, this function replaces up to
+point-max (as is appropriate for completing a file name).  If
+GOTO-END is non-nil, however, it instead replaces up to END."
   (or beg (setq beg (minibuffer-prompt-end)))
   (or end (setq end (point-max)))
-  (let* ((table minibuffer-completion-table)
+  (let* ((table (if (eq minibuffer-completion-table 'read-file-name-internal)
+                    'PC-read-file-name-internal
+                    minibuffer-completion-table))
         (pred minibuffer-completion-predicate)
         (filename (funcall PC-completion-as-file-name-predicate))
-        (dirname nil)
-        (dirlength 0)
+        (dirname nil) ; non-nil only if a filename is being completed
+        ;; The following used to be "(dirlength 0)" which caused the erasure of
+        ;; the entire buffer text before `point' when inserting a completion
+        ;; into a buffer.
+        dirlength
         (str (buffer-substring beg end))
         (incname (and filename (string-match "<\\([^\"<>]*\\)>?$" str)))
         (ambig nil)
-        basestr
+        basestr origstr
         env-on
         regex
         p offset
+         abbreviated
         (poss nil)
         helpposs
         (case-fold-search completion-ignore-case))
@@ -397,8 +491,9 @@ of `minibuffer-completion-table' and the minibuffer contents.")
       (and filename
            (let ((dir (file-name-directory str))
                  (file (file-name-nondirectory str))
-                ;; The base dir for file-completion is passed in `predicate'.
-                (default-directory (expand-file-name pred)))
+                ;; The base dir for file-completion was passed in `predicate'.
+                (default-directory (if (stringp pred) (expand-file-name pred)
+                                      default-directory)))
              (while (and (stringp dir) (not (file-directory-p dir)))
                (setq dir (directory-file-name dir))
                (setq file (concat (replace-regexp-in-string
@@ -406,14 +501,15 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                                    (file-name-nondirectory dir))
                                   "*/" file))
                (setq dir (file-name-directory dir)))
-             (setq str (concat dir file))))
+             (setq origstr str str (concat dir file))))
 
       ;; Look for wildcard expansions in directory name
       (and filename
           (string-match "\\*.*/" str)
           (let ((pat str)
-                ;; The base dir for file-completion is passed in `predicate'.
-                (default-directory (expand-file-name pred))
+                ;; The base dir for file-completion was passed in `predicate'.
+                (default-directory (if (stringp pred) (expand-file-name pred)
+                                      default-directory))
                 files)
             (setq p (1+ (string-match "/[^/]*\\'" pat)))
             (while (setq p (string-match PC-delim-regex pat p))
@@ -421,20 +517,29 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                                 "*"
                                 (substring pat p))
                     p (+ p 2)))
-            (setq files (PC-expand-many-files (concat pat "*")))
+            (setq files (file-expand-wildcards (concat pat "*")))
             (if files
                 (let ((dir (file-name-directory (car files)))
                       (p files))
                   (while (and (setq p (cdr p))
                               (equal dir (file-name-directory (car p)))))
                   (if p
-                      (setq filename nil table nil pred nil
+                      (setq filename nil table nil
+                             pred (if (stringp pred) nil pred)
                             ambig t)
                     (delete-region beg end)
                     (setq str (concat dir (file-name-nondirectory str)))
                     (insert str)
                     (setq end (+ beg (length str)))))
-              (setq filename nil table nil pred nil))))
+              (if origstr
+                       ;; If the wildcards were introduced by us, it's
+                       ;; possible that PC-read-file-name-internal can
+                       ;; still find matches for the original string
+                       ;; even if we couldn't, so remove the added
+                       ;; wildcards.
+                   (setq str origstr)
+                (setq filename nil table nil
+                       pred (if (stringp pred) nil pred))))))
 
       ;; Strip directory name if appropriate
       (if filename
@@ -486,33 +591,105 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                  pred nil))
 
       ;; Find an initial list of possible completions
-      (if (not (setq p (string-match (concat PC-delim-regex
+        (unless (setq p (string-match (concat PC-delim-regex
                                             (if filename "\\|\\*" ""))
                                     str
-                                    (+ (length dirname) offset))))
+                                      (+ (length dirname) offset)))
 
          ;; Minibuffer contains no hyphens -- simple case!
-         (setq poss (all-completions (if env-on
-                                         basestr str)
+          (setq poss (all-completions (if env-on basestr str)
                                      table
                                      pred))
-
+          (unless (or poss (string-equal str ""))
+            ;; Try completion as an abbreviation, e.g. "mvb" ->
+            ;; "m-v-b" -> "multiple-value-bind", but only for
+            ;; non-empty strings.
+            (setq origstr str
+                  abbreviated t)
+            (if filename
+                (cond
+                  ;; "alpha" or "/alpha" -> expand whole path.
+                  ((string-match "^/?\\([A-Za-z0-9]+\\)$" str)
+                   (setq
+                    basestr ""
+                    p nil
+                   poss (file-expand-wildcards
+                          (concat "/"
+                                  (mapconcat #'list (match-string 1 str) "*/")
+                                  "*"))
+                    beg (1- beg)))
+                  ;; Alphanumeric trailer -> expand trailing file
+                  ((string-match "^\\(.+/\\)\\([A-Za-z0-9]+\\)$" str)
+                   (setq regex (concat "\\`"
+                                        (mapconcat #'list
+                                                   (match-string 2 str)
+                                                   "[A-Za-z0-9]*[^A-Za-z0-9]"))
+                          p (1+ (length (match-string 1 str))))))
+             (setq regex (concat "\\`" (mapconcat (lambda (c)
+                                                    (regexp-quote (string c)))
+                                                  str "[^-]*-"))
+                   p 1))))
+        (when p
        ;; Use all-completions to do an initial cull.  This is a big win,
        ;; since all-completions is written in C!
        (let ((compl (all-completions (if env-on
                                          (file-name-nondirectory (substring str 0 p))
                                        (substring str 0 p))
-                                        table
-                                        pred)))
+                                      table
+                                      pred)))
          (setq p compl)
+            (when (and compl abbreviated)
+              (if filename
+                  (progn
+                    (setq p nil)
+                    (dolist (x compl)
+                      (when (string-match regex x)
+                        (push x p)))
+                    (setq basestr (try-completion "" p)))
+                  (setq basestr (mapconcat 'list str "-"))
+                  (delete-region beg end)
+                  (setq end (+ beg (length basestr)))
+                  (insert basestr))))
          (while p
            (and (string-match regex (car p))
                 (progn
                   (set-text-properties 0 (length (car p)) '() (car p))
                   (setq poss (cons (car p) poss))))
-           (setq p (cdr p)))))
+            (setq p (cdr p))))
+
+      ;; If table had duplicates, they can be here.
+      (delete-dups poss)
+
+      ;; Handle completion-ignored-extensions
+      (and filename
+           (not (eq mode 'help))
+           (let ((p2 poss))
+
+             ;; Build a regular expression representing the extensions list
+             (or (equal completion-ignored-extensions PC-ignored-extensions)
+                 (setq PC-ignored-regexp
+                       (concat "\\("
+                               (mapconcat
+                                'regexp-quote
+                                (setq PC-ignored-extensions
+                                      completion-ignored-extensions)
+                                "\\|")
+                               "\\)\\'")))
+
+             ;; Check if there are any without an ignored extension.
+             ;; Also ignore `.' and `..'.
+             (setq p nil)
+             (while p2
+               (or (string-match PC-ignored-regexp (car p2))
+                   (string-match "\\(\\`\\|/\\)[.][.]?/?\\'" (car p2))
+                   (setq p (cons (car p2) p)))
+               (setq p2 (cdr p2)))
+
+             ;; If there are "good" names, use them
+             (and p (setq poss p))))
 
       ;; Now we have a list of possible completions
+
       (cond
 
        ;; No valid completions found
@@ -522,6 +699,9 @@ of `minibuffer-completion-table' and the minibuffer contents.")
            (let ((PC-word-failed-flag t))
              (delete-backward-char 1)
              (PC-do-completion 'word))
+               (when abbreviated
+                 (delete-region beg end)
+                 (insert origstr))
          (beep)
          (PC-temp-minibuffer-message (if ambig
                                          " [Ambiguous dir name]"
@@ -534,34 +714,6 @@ of `minibuffer-completion-table' and the minibuffer contents.")
        ((or (cdr (setq helpposs poss))
            (memq mode '(help word)))
 
-       ;; Handle completion-ignored-extensions
-       (and filename
-            (not (eq mode 'help))
-            (let ((p2 poss))
-
-              ;; Build a regular expression representing the extensions list
-              (or (equal completion-ignored-extensions PC-ignored-extensions)
-                  (setq PC-ignored-regexp
-                        (concat "\\("
-                                (mapconcat
-                                 'regexp-quote
-                                 (setq PC-ignored-extensions
-                                       completion-ignored-extensions)
-                                 "\\|")
-                                "\\)\\'")))
-
-              ;; Check if there are any without an ignored extension.
-              ;; Also ignore `.' and `..'.
-              (setq p nil)
-              (while p2
-                (or (string-match PC-ignored-regexp (car p2))
-                    (string-match "\\(\\`\\|/\\)[.][.]?/?\\'" (car p2))
-                    (setq p (cons (car p2) p)))
-                (setq p2 (cdr p2)))
-
-              ;; If there are "good" names, use them
-              (and p (setq poss p))))
-
        ;; Is the actual string one of the possible completions?
        (setq p (and (not (eq mode 'help)) poss))
        (while (and p
@@ -582,51 +734,52 @@ of `minibuffer-completion-table' and the minibuffer contents.")
 
            ;; Check if next few letters are the same in all cases
            (if (and (not (eq mode 'help))
-                    (setq prefix (try-completion "" (mapcar 'list poss))))
+                    (setq prefix (PC-try-completion
+                                  (PC-chunk-after basestr skip) poss)))
                (let ((first t) i)
                  (if (eq mode 'word)
                      (setq prefix (PC-chop-word prefix basestr)))
                  (goto-char (+ beg (length dirname)))
                  (while (and (progn
-                               (setq i 0)
+                               (setq i 0) ; index into prefix string
                                (while (< i (length prefix))
                                  (if (and (< (point) end)
-                                          (eq (aref prefix i)
-                                              (following-char)))
-                                     (forward-char 1)
-                                   (if (and (< (point) end)
-                                            (or (and (looking-at " ")
-                                                     (memq (aref prefix i)
-                                                           PC-delims-list))
-                                                (eq (downcase (aref prefix i))
-                                                    (downcase
-                                                     (following-char)))))
-                                       (progn
-                                         (delete-char 1)
-                                         (setq end (1- end)))
+                                           (or (eq (downcase (aref prefix i))
+                                                  (downcase (following-char)))
+                                              (and (looking-at " ")
+                                                   (memq (aref prefix i)
+                                                         PC-delims-list))))
+                                     ;; replace " " by the actual delimiter
+                                      ;; or input char by prefix char
+                                     (progn
+                                       (delete-char 1)
+                                       (insert (substring prefix i (1+ i))))
+                                   ;; insert a new character
+                                   (progn
                                      (and filename (looking-at "\\*")
                                           (progn
                                             (delete-char 1)
                                             (setq end (1- end))))
-                                     (setq improved t))
-                                   (insert (substring prefix i (1+ i)))
-                                   (setq end (1+ end)))
+                                     (setq improved t)
+                                     (insert (substring prefix i (1+ i)))
+                                     (setq end (1+ end))))
                                  (setq i (1+ i)))
                                (or pt (setq pt (point)))
                                (looking-at PC-delim-regex))
                              (setq skip (concat skip
                                                 (regexp-quote prefix)
                                                 PC-ndelims-regex)
-                                   prefix (try-completion
-                                           ""
+                                   prefix (PC-try-completion
+                                           (PC-chunk-after
+                                            ;; not basestr, because that does
+                                            ;; not reflect insertions
+                                            (buffer-substring
+                                             (+ beg (length dirname)) end)
+                                            skip)
                                            (mapcar
-                                            (function
-                                             (lambda (x)
-                                               (list
-                                                (and (string-match skip x)
-                                                     (substring
-                                                      x
-                                                      (match-end 0))))))
+                                             (lambda (x)
+                                               (when (string-match skip x)
+                                                 (substring x (match-end 0))))
                                             poss)))
                              (or (> i 0) (> (length prefix) 0))
                              (or (not (eq mode 'word))
@@ -642,35 +795,58 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                (if improved
 
                    ;; We changed it... would it be complete without the space?
-                   (if (test-completion (buffer-substring 1 (1- end))
-                                         table pred)
+                   (if (test-completion (buffer-substring
+                                          (field-beginning) (1- end))
+                                         table pred)
                        (delete-region (1- end) end)))
 
              (if improved
 
                  ;; We changed it... enough to be complete?
                  (and (eq mode 'exit)
-                      (test-completion (field-string) table pred))
+                      (test-completion-ignore-case (field-string) table pred))
 
                ;; If totally ambiguous, display a list of completions
                (if (or (eq completion-auto-help t)
                        (and completion-auto-help
                             (eq last-command this-command))
                        (eq mode 'help))
-                   (with-output-to-temp-buffer "*Completions*"
-                     (display-completion-list (sort helpposs 'string-lessp))
-                     (with-current-buffer standard-output
-                       ;; Record which part of the buffer we are completing
-                       ;; so that choosing a completion from the list
-                       ;; knows how much old text to replace.
-                       (setq completion-base-size dirlength)))
-                 (PC-temp-minibuffer-message " [Next char not unique]"))
-               nil)))))
+                    (let ((prompt-end (minibuffer-prompt-end)))
+                      (with-output-to-temp-buffer "*Completions*"
+                        (display-completion-list (sort helpposs 'string-lessp))
+                        (setq PC-do-completion-end end
+                              PC-goto-end goto-end)
+                        (with-current-buffer standard-output
+                          ;; Record which part of the buffer we are completing
+                          ;; so that choosing a completion from the list
+                          ;; knows how much old text to replace.
+                          ;; This was briefly nil in the non-dirname case.
+                          ;; However, if one calls PC-lisp-complete-symbol
+                          ;; on "(ne-f" with point on the hyphen, PC offers
+                          ;; all completions starting with "(ne", some of
+                          ;; which do not match the "-f" part (maybe it
+                          ;; should not, but it does). In such cases,
+                          ;; completion gets confused trying to figure out
+                          ;; how much to replace, so we tell it explicitly
+                          ;; (ie, the number of chars in the buffer before beg).
+                          ;;
+                          ;; Note that choose-completion-string-functions
+                          ;; plays around with point.
+                          (setq completion-base-size (if dirname
+                                                         dirlength
+                                                       (- beg prompt-end))))))
+                             (PC-temp-minibuffer-message " [Next char not unique]"))
+                         ;; Expansion of filenames is not reversible,
+                         ;; so just keep the prefix.
+           (when (and abbreviated filename)
+             (delete-region (point) end))
+                         nil)))))
 
        ;; Only one possible completion
        (t
        (if (and (equal basestr (car poss))
-                (not (and env-on filename)))
+                 (not (and env-on filename))
+                 (not abbreviated))
            (if (null mode)
                (PC-temp-minibuffer-message " [Sole completion]"))
          (delete-region beg end)
@@ -696,7 +872,7 @@ of `minibuffer-completion-table' and the minibuffer contents.")
 (defun PC-temp-minibuffer-message (message)
   "A Lisp version of `temp_minibuffer_message' from minibuf.c."
   (cond (PC-not-minibuffer
-        (message message)
+        (message "%s" message)
         (sit-for 2)
         (message ""))
        ((fboundp 'temp-minibuffer-message)
@@ -713,6 +889,10 @@ of `minibuffer-completion-table' and the minibuffer contents.")
               (setq quit-flag nil
                     unread-command-events '(7))))))))
 
+;; Does not need to be buffer-local (?) because only used when one
+;; PC-l-c-s immediately follows another.
+(defvar PC-lisp-complete-end nil
+  "Internal variable used by `PC-lisp-complete-symbol'.")
 
 (defun PC-lisp-complete-symbol ()
   "Perform completion on Lisp symbol preceding point.
@@ -724,7 +904,11 @@ only symbols with function definitions are considered.
 Otherwise, all symbols with function definitions, values
 or properties are considered."
   (interactive)
-  (let* ((end (point))
+  (let* ((end
+          (save-excursion
+            (with-syntax-table lisp-mode-syntax-table
+              (skip-syntax-forward "_w")
+              (point))))
         (beg (save-excursion
                 (with-syntax-table lisp-mode-syntax-table
                   (backward-sexp 1)
@@ -739,7 +923,39 @@ or properties are considered."
                        (or (boundp sym) (fboundp sym)
                            (symbol-plist sym))))))
         (PC-not-minibuffer t))
-    (PC-do-completion nil beg end)))
+    ;; http://lists.gnu.org/archive/html/emacs-devel/2007-03/msg01211.html
+    ;;
+    ;; This deals with cases like running PC-l-c-s on "M-: (n-f".
+    ;; The first call to PC-l-c-s expands this to "(ne-f", and moves
+    ;; point to the hyphen [1]. If one calls PC-l-c-s immediately after,
+    ;; then without the last-command check, one is offered all
+    ;; completions of "(ne", which is presumably not what one wants.
+    ;;
+    ;; This is arguably (at least, it seems to be the existing intended
+    ;; behavior) what one _does_ want if point has been explicitly
+    ;; positioned on the hyphen. Note that if PC-do-completion (qv) binds
+    ;; completion-base-size to nil, then completion does not replace the
+    ;; correct amount of text in such cases.
+    ;;
+    ;; Neither of these problems occur when using PC for filenames in the
+    ;; minibuffer, because in that case PC-do-completion is called without
+    ;; an explicit value for END, and so uses (point-max). This is fine for
+    ;; a filename, because the end of the filename must be at the end of
+    ;; the minibuffer. The same is not true for lisp symbols.
+    ;;
+    ;; [1] An alternate fix would be to not move point to the hyphen
+    ;; in such cases, but that would make the behavior different from
+    ;; that for filenames. It seems PC moves point to the site of the
+    ;; first difference between the possible completions.
+    ;;
+    ;; Alternatively alternatively, maybe end should be computed in
+    ;; the same way as beg. That would change the behavior though.
+    (if (equal last-command 'PC-lisp-complete-symbol)
+        (PC-do-completion nil beg PC-lisp-complete-end t)
+      (if PC-lisp-complete-end
+          (move-marker PC-lisp-complete-end end)
+        (setq PC-lisp-complete-end (copy-marker end t)))
+      (PC-do-completion nil beg end t))))
 
 (defun PC-complete-as-file-name ()
    "Perform completion on file names preceding point.
@@ -750,48 +966,12 @@ or properties are considered."
                                       (point-min) t)
                    (+ (point) 2)
                    (point-min)))
-          (minibuffer-completion-table 'read-file-name-internal)
-          (minibuffer-completion-predicate "")
+          (minibuffer-completion-table 'PC-read-file-name-internal)
+          (minibuffer-completion-predicate nil)
           (PC-not-minibuffer t))
      (goto-char end)
      (PC-do-completion nil beg end)))
 
-;; Use the shell to do globbing.
-;; This could now use file-expand-wildcards instead.
-
-(defun PC-expand-many-files (name)
-  (with-current-buffer (generate-new-buffer " *Glob Output*")
-    (erase-buffer)
-    (shell-command (concat "echo " name) t)
-    (goto-char (point-min))
-    (if (looking-at ".*No match")
-       nil
-      (insert "(\"")
-      (while (search-forward " " nil t)
-       (delete-backward-char 1)
-       (insert "\" \""))
-      (goto-char (point-max))
-      (delete-backward-char 1)
-      (insert "\")")
-      (goto-char (point-min))
-      (let ((files (read (current-buffer))) (p nil))
-       (kill-buffer (current-buffer))
-       (or (equal completion-ignored-extensions PC-ignored-extensions)
-           (setq PC-ignored-regexp
-                 (concat "\\("
-                         (mapconcat
-                          'regexp-quote
-                          (setq PC-ignored-extensions
-                                completion-ignored-extensions)
-                          "\\|")
-                         "\\)\\'")))
-       (setq p nil)
-       (while files
-         (or (string-match PC-ignored-regexp (car files))
-             (setq p (cons (car files) p)))
-         (setq files (cdr files)))
-       p))))
-
 ;; Facilities for loading C header files.  This is independent from the
 ;; main completion code.  See also the variable `PC-include-file-path'
 ;; at top of this file.
@@ -917,20 +1097,23 @@ absolute rather than relative to some directory on the SEARCH-PATH."
          (setq sorted (cdr sorted)))
        compressed))))
 
-(defun PC-read-include-file-name-internal (string dir action)
-  (if (string-match "<\\([^\"<>]*\\)>?$" string)
-      (let* ((name (substring string (match-beginning 1) (match-end 1)))
+(defun PC-read-file-name-internal (string pred action)
+  "Extend `read-file-name-internal' to handle include files.
+This is only used by "
+  (if (string-match "<\\([^\"<>]*\\)>?\\'" string)
+      (let* ((name (match-string 1 string))
             (str2 (substring string (match-beginning 0)))
             (completion-table
-             (mapcar (function (lambda (x) (list (format "<%s>" x))))
+             (mapcar (lambda (x)
+                        (format (if (string-match "/\\'" x) "<%s" "<%s>") x))
                      (PC-include-file-all-completions
                       name (PC-include-file-path)))))
-       (cond
-        ((not completion-table) nil)
-        ((eq action nil) (try-completion str2 completion-table nil))
-        ((eq action t) (all-completions str2 completion-table nil))
-        ((eq action 'lambda) (test-completion str2 completion-table nil))))
-    (funcall PC-old-read-file-name-internal string dir action)))
+        (cond
+         ((not completion-table) nil)
+         ((eq action 'lambda) (test-completion str2 completion-table nil))
+         ((eq action nil) (PC-try-completion str2 completion-table nil))
+         ((eq action t) (all-completions str2 completion-table nil))))
+    (read-file-name-internal string pred action)))
 \f
 
 (provide 'complete)