]> code.delx.au - gnu-emacs/blobdiff - lisp/pcomplete.el
* lisp/net/tramp-gvfs.el (tramp-gvfs-mount-spec): Fix typo.
[gnu-emacs] / lisp / pcomplete.el
index 753e779c3b8da20cf9e3ede433698df943c876a4..41e3be19054da6d921587e886847c7a9f0c2855a 100644 (file)
@@ -1,7 +1,6 @@
-;;; pcomplete.el --- programmable completion
+;;; pcomplete.el --- programmable completion -*- lexical-binding: t -*-
 
-;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
-;;   2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
 ;; Keywords: processes abbrev
@@ -29,7 +28,7 @@
 ;; argument position.
 ;;
 ;; To use pcomplete with shell-mode, for example, you will need the
-;; following in your .emacs file:
+;; following in your init file:
 ;;
 ;;   (add-hook 'shell-mode-hook 'pcomplete-shell-setup)
 ;;
 
 ;;; Code:
 
-(eval-when-compile (require 'cl))
+(require 'comint)
 
 (defgroup pcomplete nil
   "Programmable completion."
@@ -155,6 +154,7 @@ This mirrors the optional behavior of tcsh."
   "A list of characters which constitute a proper suffix."
   :type '(repeat character)
   :group 'pcomplete)
+(make-obsolete-variable 'pcomplete-suffix-list nil "24.1")
 
 (defcustom pcomplete-recexact nil
   "If non-nil, use shortest completion if characters cannot be added.
@@ -164,22 +164,8 @@ A non-nil value is useful if `pcomplete-autolist' is non-nil too."
   :type 'boolean
   :group 'pcomplete)
 
-(defcustom pcomplete-arg-quote-list nil
-  "List of characters to quote when completing an argument."
-  :type '(choice (repeat character)
-                (const :tag "Don't quote" nil))
-  :group 'pcomplete)
-
-(defcustom pcomplete-quote-arg-hook nil
-  "A hook which is run to quote a character within a filename.
-Each function is passed both the filename to be quoted, and the index
-to be considered.  If the function wishes to provide an alternate
-quoted form, it need only return the replacement string.  If no
-function provides a replacement, quoting shall proceed as normal,
-using a backslash to quote any character which is a member of
-`pcomplete-arg-quote-list'."
-  :type 'hook
-  :group 'pcomplete)
+(define-obsolete-variable-alias
+  'pcomplete-arg-quote-list 'comint-file-name-quote-list "24.3")
 
 (defcustom pcomplete-man-function 'man
   "A function to that will be called to display a manual page.
@@ -207,7 +193,7 @@ current command argument."
 
 (defcustom pcomplete-expand-before-complete nil
   "If non-nil, expand the current argument before completing it.
-This means that typing something such as '$HOME/bi' followed by
+This means that typing something such as `$HOME/bi' followed by
 \\[pcomplete-argument] will cause the variable reference to be
 resolved first, and the resultant value that will be completed against
 to be inserted in the buffer.  Note that exactly what gets expanded
@@ -349,6 +335,16 @@ modified to be an empty string, or the desired separation string."
 (defvar pcomplete-show-list nil)
 (defvar pcomplete-expand-only-p nil)
 
+;; for the sake of the bye-compiler, when compiling other files that
+;; contain completion functions
+(defvar pcomplete-args nil)
+(defvar pcomplete-begins nil)
+(defvar pcomplete-last nil)
+(defvar pcomplete-index nil)
+(defvar pcomplete-stub nil)
+(defvar pcomplete-seen nil)
+(defvar pcomplete-norm-func nil)
+
 ;;; User Functions:
 
 ;;; Alternative front-end using the standard completion facilities.
@@ -359,97 +355,36 @@ modified to be an empty string, or the desired separation string."
 ;; it pretty much impossible to have completion other than
 ;; prefix-completion.
 ;;
-;; pcomplete--common-quoted-suffix and pcomplete--table-subvert try to
-;; work around this difficulty with heuristics, but it's
-;; really a hack.
+;; pcomplete--common-suffix and completion-table-subvert try to work around
+;; this difficulty with heuristics, but it's really a hack.
+
+(defvar pcomplete-unquote-argument-function #'comint--unquote-argument)
 
-(defvar pcomplete-unquote-argument-function nil)
+(defsubst pcomplete-unquote-argument (s)
+  (funcall pcomplete-unquote-argument-function s))
 
-(defun pcomplete-unquote-argument (s)
-  (cond
-   (pcomplete-unquote-argument-function
-    (funcall pcomplete-unquote-argument-function s))
-   ((null pcomplete-arg-quote-list) s)
-   (t
-    (replace-regexp-in-string "\\\\\\(.\\)" "\\1" s t))))
+(defvar pcomplete-requote-argument-function #'comint--requote-argument)
 
 (defun pcomplete--common-suffix (s1 s2)
-  (assert (not (or (string-match "\n" s1) (string-match "\n" s2))))
   ;; Since S2 is expected to be the "unquoted/expanded" version of S1,
   ;; there shouldn't be any case difference, even if the completion is
   ;; case-insensitive.
-  (let ((case-fold-search nil)) ;; pcomplete-ignore-case
-    (string-match ".*?\\(.*\\)\n.*\\1\\'" (concat s1 "\n" s2))
+  (let ((case-fold-search nil))
+    (string-match
+     ;; \x3FFF7F is just an arbitrary char among the ones Emacs accepts
+     ;; that hopefully will never appear in normal text.
+     "\\(?:.\\|\n\\)*?\\(\\(?:.\\|\n\\)*\\)\x3FFF7F\\(?:.\\|\n\\)*\\1\\'"
+     (concat s1 "\x3FFF7F" s2))
     (- (match-end 1) (match-beginning 1))))
 
-(defun pcomplete--common-quoted-suffix (s1 s2)
-  "Find the common suffix between S1 and S2 where S1 is the expanded S2.
-S1 is expected to be the unquoted and expanded version of S1.
-Returns (PS1 . PS2), i.e. the shortest prefixes of S1 and S2, such that
-S1 = (concat PS1 SS1) and S2 = (concat PS2 SS2) and
-SS1 = (unquote SS2)."
-  (let* ((cs (pcomplete--common-suffix s1 s2))
-         (ss1 (substring s1 (- (length s1) cs)))
-         (qss1 (pcomplete-quote-argument ss1))
-         qc)
-    (if (and (not (equal ss1 qss1))
-             (setq qc (pcomplete-quote-argument (substring ss1 0 1)))
-             (eq t (compare-strings s2 (- (length s2) cs (length qc) -1)
-                                    (- (length s2) cs -1)
-                                    qc nil nil)))
-        ;; The difference found is just that one char is quoted in S2
-        ;; but not in S1, keep looking before this difference.
-        (pcomplete--common-quoted-suffix
-         (substring s1 0 (- (length s1) cs))
-         (substring s2 0 (- (length s2) cs (length qc) -1)))
-      (cons (substring s1 0 (- (length s1) cs))
-            (substring s2 0 (- (length s2) cs))))))
-
-(defun pcomplete--table-subvert (table s1 s2 string pred action)
-  "Completion table that replaces the prefix S1 with S2 in STRING.
-When TABLE, S1 and S2 are provided by `apply-partially', the result
-is a completion table which completes strings of the form (concat S1 S)
-in the same way as TABLE completes strings of the form (concat S2 S)."
-  (let* ((str (if (eq t (compare-strings string 0 (length s1) s1 nil nil
-                                         completion-ignore-case))
-                  (concat s2 (pcomplete-unquote-argument
-                              (substring string (length s1))))))
-         (res (if str (complete-with-action action table str pred))))
-    (when res
-      (cond
-       ((and (eq (car-safe action) 'boundaries))
-        (let ((beg (or (and (eq (car-safe res) 'boundaries) (cadr res)) 0)))
-          (list* 'boundaries
-                 (max (length s1)
-                      ;; FIXME: Adjust because of quoting/unquoting.
-                      (+ beg (- (length s1) (length s2))))
-                 (and (eq (car-safe res) 'boundaries) (cddr res)))))
-       ((stringp res)
-        (if (eq t (compare-strings res 0 (length s2) s2 nil nil
-                                   completion-ignore-case))
-            (concat s1 (pcomplete-quote-argument
-                        (substring res (length s2))))))
-       ((eq action t)
-        (let ((bounds (completion-boundaries str table pred "")))
-          (if (>= (car bounds) (length s2))
-              res
-            (let ((re (concat "\\`"
-                              (regexp-quote (substring s2 (car bounds))))))
-              (delq nil
-                    (mapcar (lambda (c)
-                              (if (string-match re c)
-                                  (substring c (match-end 0))))
-                            res))))))))))
-        
-;; I don't think such commands are usable before first setting up buffer-local
-;; variables to parse args, so there's no point autoloading it.
-;; ;;;###autoload
-(defun pcomplete-std-complete ()
+(defun pcomplete-completions-at-point ()
   "Provide standard completion using pcomplete's completion tables.
 Same as `pcomplete' but using the standard completion UI."
-  (interactive)
   ;; FIXME: it only completes the text before point, whereas the
   ;; standard UI may also consider text after point.
+  ;; FIXME: the `pcomplete' UI may be used internally during
+  ;; pcomplete-completions and then throw to `pcompleted', thus
+  ;; imposing the pcomplete UI over the standard UI.
   (catch 'pcompleted
     (let* ((pcomplete-stub)
            pcomplete-seen pcomplete-norm-func
@@ -467,67 +402,73 @@ Same as `pcomplete' but using the standard completion UI."
            ;; pcomplete-parse-arguments-function does, that connection
            ;; might not be that close.  E.g. in eshell,
            ;; pcomplete-parse-arguments-function expands envvars.
-           ;; 
+           ;;
            ;; Since we use minibuffer-complete, which doesn't know
            ;; pcomplete-stub and works from the buffer's text instead,
            ;; we need to trick minibuffer-complete, into using
            ;; pcomplete-stub without its knowledge.  To that end, we
-           ;; use pcomplete--table-subvert to construct a completion
+           ;; use completion-table-subvert to construct a completion
            ;; table which expects strings using a prefix from the
            ;; buffer's text but internally uses the corresponding
            ;; prefix from pcomplete-stub.
            (beg (max (- (point) (length pcomplete-stub))
                      (pcomplete-begin)))
-           (buftext (buffer-substring beg (point)))
-           (table
-            (cond
-             ((null completions) nil)
-             ((not (equal pcomplete-stub buftext))
-              ;; This isn't always strictly right (e.g. if
-              ;; FOO="toto/$FOO", then completion of /$FOO/bar may
-              ;; result in something incorrect), but given the lack of
-              ;; any other info, it's about as good as it gets, and in
-              ;; practice it should work just fine (fingers crossed).
-              (let ((prefixes (pcomplete--common-quoted-suffix
-                               pcomplete-stub buftext)))
-                (apply-partially
-                 'pcomplete--table-subvert
-                 completions
-                 (cdr prefixes) (car prefixes))))
-             (t
-              (lexical-let ((completions completions))
-                (lambda (string pred action)
-                  (let ((res (complete-with-action
-                              action completions string pred)))
-                    (if (stringp res)
-                        (pcomplete-quote-argument res)
-                      res)))))))
-           (pred
-            ;; pare it down, if applicable
-            (when (and table pcomplete-use-paring pcomplete-seen)
-              (setq pcomplete-seen
-                    (mapcar (lambda (f)
-                              (funcall pcomplete-norm-func
-                                       (directory-file-name f)))
-                            pcomplete-seen))
-              (lambda (f)
-                (not (member
-                      (funcall pcomplete-norm-func
-                               (directory-file-name f))
-                      pcomplete-seen))))))
-
-      (completion-in-region
-       beg (point)
-       ;; Add a space at the end of completion.  Use a terminator-regexp
-       ;; that never matches since the terminator cannot appear
-       ;; within the completion field anyway.
-       (if (zerop (length pcomplete-termination-string))
-           table
-         (apply-partially 'completion-table-with-terminator
-                          (cons pcomplete-termination-string
-                                "\\`a\\`")
-                          table))
-       pred))))
+           (buftext (pcomplete-unquote-argument
+                     (buffer-substring beg (point)))))
+      (when completions
+        (let ((table
+               (completion-table-with-quoting
+                (if (equal pcomplete-stub buftext)
+                    completions
+                  ;; This may not always be strictly right, but given the lack
+                  ;; of any other info, it's about as good as it gets, and in
+                  ;; practice it should work just fine (fingers crossed).
+                  (let ((suf-len (pcomplete--common-suffix
+                                  pcomplete-stub buftext)))
+                    (completion-table-subvert
+                     completions
+                     (substring buftext 0 (- (length buftext) suf-len))
+                     (substring pcomplete-stub 0
+                                (- (length pcomplete-stub) suf-len)))))
+                pcomplete-unquote-argument-function
+                pcomplete-requote-argument-function))
+              (pred
+               ;; Pare it down, if applicable.
+               (when (and pcomplete-use-paring pcomplete-seen)
+                 ;; Capture the dynbound values for later use.
+                 (let ((norm-func pcomplete-norm-func)
+                       (seen
+                       (mapcar (lambda (f)
+                                 (funcall pcomplete-norm-func
+                                          (directory-file-name f)))
+                               pcomplete-seen)))
+                   (lambda (f)
+                     (not (member
+                           (funcall norm-func (directory-file-name f))
+                           seen)))))))
+          (when pcomplete-ignore-case
+            (setq table (completion-table-case-fold table)))
+          (list beg (point) table
+                :predicate pred
+                :exit-function
+               ;; If completion is finished, add a terminating space.
+               ;; We used to also do this if STATUS is `sole', but
+               ;; that does not work right when completion cycling.
+                (unless (zerop (length pcomplete-termination-string))
+                  (lambda (_s status)
+                    (when (eq status 'finished)
+                      (if (looking-at
+                           (regexp-quote pcomplete-termination-string))
+                          (goto-char (match-end 0))
+                        (insert pcomplete-termination-string)))))))))))
+
+ ;; I don't think such commands are usable before first setting up buffer-local
+ ;; variables to parse args, so there's no point autoloading it.
+ ;; ;;;###autoload
+(defun pcomplete-std-complete ()
+  (let ((data (pcomplete-completions-at-point)))
+    (completion-in-region (nth 0 data) (nth 1 data) (nth 2 data)
+                          (plist-get :predicate (nthcdr 3 data)))))
 
 ;;; Pcomplete's native UI.
 
@@ -544,7 +485,7 @@ completion functions list (it should occur fairly early in the list)."
                                pcomplete-expand-and-complete
                                pcomplete-reverse)))
       (progn
-       (delete-backward-char pcomplete-last-completion-length)
+       (delete-char (- pcomplete-last-completion-length))
        (if (eq this-command 'pcomplete-reverse)
            (progn
               (push (car (last pcomplete-current-completions))
@@ -607,7 +548,7 @@ This will modify the current buffer."
     (pcomplete)
     (when (and pcomplete-current-completions
               (> (length pcomplete-current-completions) 0)) ;??
-      (delete-backward-char pcomplete-last-completion-length)
+      (delete-char (- pcomplete-last-completion-length))
       (while pcomplete-current-completions
        (unless (pcomplete-insert-entry
                 "" (car pcomplete-current-completions) t
@@ -630,7 +571,7 @@ This will modify the current buffer."
   (when (and pcomplete-cycle-completions
             pcomplete-current-completions
             (eq last-command 'pcomplete-argument))
-    (delete-backward-char pcomplete-last-completion-length)
+    (delete-char (- pcomplete-last-completion-length))
     (setq pcomplete-current-completions nil
          pcomplete-last-completion-raw nil))
   (let ((pcomplete-show-list t))
@@ -639,17 +580,6 @@ This will modify the current buffer."
 ;;; Internal Functions:
 
 ;; argument handling
-
-;; for the sake of the bye-compiler, when compiling other files that
-;; contain completion functions
-(defvar pcomplete-args nil)
-(defvar pcomplete-begins nil)
-(defvar pcomplete-last nil)
-(defvar pcomplete-index nil)
-(defvar pcomplete-stub nil)
-(defvar pcomplete-seen nil)
-(defvar pcomplete-norm-func nil)
-
 (defun pcomplete-arg (&optional index offset)
   "Return the textual content of the INDEXth argument.
 INDEX is based from the current processing position.  If INDEX is
@@ -771,10 +701,14 @@ dynamic-complete-functions are kept.  For comint mode itself,
 this is `comint-dynamic-complete-functions'."
   (set (make-local-variable 'pcomplete-parse-arguments-function)
        'pcomplete-parse-comint-arguments)
+  (add-hook 'completion-at-point-functions
+            'pcomplete-completions-at-point nil 'local)
   (set (make-local-variable completef-sym)
        (copy-sequence (symbol-value completef-sym)))
   (let* ((funs (symbol-value completef-sym))
-        (elem (or (memq 'shell-dynamic-complete-filename funs)
+        (elem (or (memq 'comint-filename-completion funs)
+                   (memq 'shell-filename-completion funs)
+                   (memq 'shell-dynamic-complete-filename funs)
                   (memq 'comint-dynamic-complete-filename funs))))
     (if elem
        (setcar elem 'pcomplete)
@@ -790,6 +724,7 @@ this is `comint-dynamic-complete-functions'."
 
 (defun pcomplete-parse-comint-arguments ()
   "Parse whitespace separated arguments in the current region."
+  (declare (obsolete comint-parse-pcomplete-arguments "24.1"))
   (let ((begin (save-excursion (comint-bol nil) (point)))
        (end (point))
        begins args)
@@ -798,12 +733,14 @@ this is `comint-dynamic-complete-functions'."
       (while (< (point) end)
        (skip-chars-forward " \t\n")
        (push (point) begins)
-       (let ((skip t))
-         (while skip
-           (skip-chars-forward "^ \t\n")
-           (if (eq (char-before) ?\\)
-               (skip-chars-forward " \t\n")
-             (setq skip nil))))
+        (while
+            (progn
+              (skip-chars-forward "^ \t\n\\")
+              (when (eq (char-after) ?\\)
+                (forward-char 1)
+                (unless (eolp)
+                  (forward-char 1)
+                  t))))
        (push (buffer-substring-no-properties (car begins) (point))
               args))
       (cons (nreverse args) (nreverse begins)))))
@@ -818,8 +755,7 @@ this is `comint-dynamic-complete-functions'."
            pcomplete-index 0
            pcomplete-stub (pcomplete-arg 'last))
       (let ((begin (pcomplete-begin 'last)))
-       (if (and pcomplete-cycle-completions
-                (listp pcomplete-stub) ;??
+       (if (and (listp pcomplete-stub) ;??
                 (not pcomplete-expand-only-p))
            (let* ((completions pcomplete-stub) ;??
                   (common-stub (car completions))
@@ -855,36 +791,54 @@ this is `comint-dynamic-complete-functions'."
              (throw 'pcompleted t)
            pcomplete-args))))))
 
-(defun pcomplete-quote-argument (filename)
-  "Return FILENAME with magic characters quoted.
-Magic characters are those in `pcomplete-arg-quote-list'."
-  (if (null pcomplete-arg-quote-list)
-      filename
-    (let ((index 0))
-      (mapconcat (lambda (c)
-                   (prog1
-                       (or (run-hook-with-args-until-success
-                            'pcomplete-quote-arg-hook filename index)
-                           (when (memq c pcomplete-arg-quote-list)
-                             (string "\\" c))
-                           (char-to-string c))
-                     (setq index (1+ index))))
-                 filename
-                 ""))))
+(define-obsolete-function-alias
+  'pcomplete-quote-argument #'comint-quote-filename "24.3")
 
 ;; file-system completion lists
 
 (defsubst pcomplete-dirs-or-entries (&optional regexp predicate)
   "Return either directories, or qualified entries."
-  ;; FIXME: pcomplete-entries doesn't return a list any more.
   (pcomplete-entries
    nil
-   (lexical-let ((re regexp)
-                 (pred predicate))
-     (lambda (f)
-       (or (file-directory-p f)
-           (and (if (not re) t (string-match re f))
-                (if (not pred) t (funcall pred f))))))))
+   (lambda (f)
+     (or (file-directory-p f)
+         (and (or (null regexp) (string-match regexp f))
+              (or (null predicate) (funcall predicate f)))))))
+
+(defun pcomplete--entries (&optional regexp predicate)
+  "Like `pcomplete-entries' but without env-var handling."
+  (let* ((ign-pred
+          (when (or pcomplete-file-ignore pcomplete-dir-ignore)
+            ;; Capture the dynbound value for later use.
+            (let ((file-ignore pcomplete-file-ignore)
+                  (dir-ignore pcomplete-dir-ignore))
+              (lambda (file)
+                (not
+                 (if (eq (aref file (1- (length file))) ?/)
+                     (and dir-ignore (string-match dir-ignore file))
+                   (and file-ignore (string-match file-ignore file))))))))
+         (reg-pred (if regexp (lambda (file) (string-match regexp file))))
+         (pred (cond
+                ((null (or ign-pred reg-pred))  predicate)
+                ((null (or ign-pred predicate)) reg-pred)
+                ((null (or reg-pred predicate)) ign-pred)
+                (t (lambda (f)
+                     (and (or (null reg-pred)  (funcall reg-pred f))
+                          (or (null ign-pred)  (funcall ign-pred f))
+                          (or (null predicate) (funcall predicate f))))))))
+    (lambda (s p a)
+      (if (and (eq a 'metadata) pcomplete-compare-entry-function)
+          `(metadata (cycle-sort-function
+                      . ,(lambda (comps)
+                           (sort comps pcomplete-compare-entry-function)))
+                     ,@(cdr (completion-file-name-table s p a)))
+        (let ((completion-ignored-extensions nil)
+             (completion-ignore-case pcomplete-ignore-case))
+          (completion-table-with-predicate
+           #'comint-completion-file-name-table pred 'strict s p a))))))
+
+(defconst pcomplete--env-regexp
+  "\\(?:\\`\\|[^\\]\\)\\(?:\\\\\\\\\\)*\\(\\$\\(?:{\\([^}]+\\)}\\|\\(?2:[[:alnum:]_]+\\)\\)\\)")
 
 (defun pcomplete-entries (&optional regexp predicate)
   "Complete against a list of directory candidates.
@@ -894,65 +848,48 @@ If PREDICATE is non-nil, it will also be used to refine the match
 \(files for which the PREDICATE returns nil will be excluded).
 If no directory information can be extracted from the completed
 component, `default-directory' is used as the basis for completion."
-  (let* ((name (substitute-env-vars pcomplete-stub))
-         (completion-ignore-case pcomplete-ignore-case)
-        (default-directory (expand-file-name
-                            (or (file-name-directory name)
-                                default-directory)))
-        above-cutoff)
-    (setq name (file-name-nondirectory name)
-         pcomplete-stub name)
-    (let ((completions
-          (file-name-all-completions name default-directory)))
-      (if regexp
-         (setq completions
-               (pcomplete-pare-list
-                completions nil
-                (function
-                 (lambda (file)
-                   (not (string-match regexp file)))))))
-      (if predicate
-         (setq completions
-               (pcomplete-pare-list
-                completions nil
-                (function
-                 (lambda (file)
-                   (not (funcall predicate file)))))))
-      (if (or pcomplete-file-ignore pcomplete-dir-ignore)
-         (setq completions
-               (pcomplete-pare-list
-                completions nil
-                (function
-                 (lambda (file)
-                   (if (eq (aref file (1- (length file)))
-                           ?/)
-                       (and pcomplete-dir-ignore
-                            (string-match pcomplete-dir-ignore file))
-                     (and pcomplete-file-ignore
-                          (string-match pcomplete-file-ignore file))))))))
-      (setq above-cutoff (and pcomplete-cycle-cutoff-length
-                            (> (length completions)
-                               pcomplete-cycle-cutoff-length)))
-      (sort completions
-           (function
-            (lambda (l r)
-              ;; for the purposes of comparison, remove the
-              ;; trailing slash from directory names.
-              ;; Otherwise, "foo.old/" will come before "foo/",
-              ;; since . is earlier in the ASCII alphabet than
-              ;; /
-              (let ((left (if (eq (aref l (1- (length l)))
-                                  ?/)
-                              (substring l 0 (1- (length l)))
-                            l))
-                    (right (if (eq (aref r (1- (length r)))
-                                   ?/)
-                               (substring r 0 (1- (length r)))
-                             r)))
-                (if above-cutoff
-                    (string-lessp left right)
-                  (funcall pcomplete-compare-entry-function
-                           left right)))))))))
+  ;; FIXME: The old code did env-var expansion here, so we reproduce this
+  ;; behavior for now, but really env-var handling should be performed globally
+  ;; rather than here since it also applies to non-file arguments.
+  (let ((table (pcomplete--entries regexp predicate)))
+    (lambda (string pred action)
+      (let ((strings nil)
+            (orig-length (length string)))
+        ;; Perform env-var expansion.
+        (while (string-match pcomplete--env-regexp string)
+          (push (substring string 0 (match-beginning 1)) strings)
+          (push (getenv (match-string 2 string)) strings)
+          (setq string (substring string (match-end 1))))
+        (if (not (and strings
+                      (or (eq action t)
+                          (eq (car-safe action) 'boundaries))))
+            (let ((newstring
+                   (mapconcat 'identity (nreverse (cons string strings)) "")))
+              ;; FIXME: We could also try to return unexpanded envvars.
+              (complete-with-action action table newstring pred))
+          (let* ((envpos (apply #'+ (mapcar #' length strings)))
+                 (newstring
+                  (mapconcat 'identity (nreverse (cons string strings)) ""))
+                 (bounds (completion-boundaries newstring table pred
+                                                (or (cdr-safe action) ""))))
+            (if (>= (car bounds) envpos)
+                ;; The env-var is "out of bounds".
+                (if (eq action t)
+                    (complete-with-action action table newstring pred)
+                  `(boundaries
+                    ,(+ (car bounds) (- orig-length (length newstring)))
+                    . ,(cdr bounds)))
+              ;; The env-var is in the file bounds.
+              (if (eq action t)
+                  (let ((comps (complete-with-action
+                                action table newstring pred))
+                        (len (- envpos (car bounds))))
+                    ;; Strip the part of each completion that's actually
+                    ;; coming from the env-var.
+                    (mapcar (lambda (s) (substring s len)) comps))
+                `(boundaries
+                  ,(+ envpos (- orig-length (length newstring)))
+                  . ,(cdr bounds))))))))))
 
 (defsubst pcomplete-all-entries (&optional regexp predicate)
   "Like `pcomplete-entries', but doesn't ignore any entries."
@@ -987,13 +924,14 @@ component, `default-directory' is used as the basis for completion."
           (pcomplete-next-arg)
           (funcall sym)))))))
 
-(defun pcomplete-opt (options &optional prefix no-ganging args-follow)
+(defun pcomplete-opt (options &optional prefix _no-ganging _args-follow)
   "Complete a set of OPTIONS, each beginning with PREFIX (?- by default).
 PREFIX may be t, in which case no PREFIX character is necessary.
 If NO-GANGING is non-nil, each option is separate (-xy is not allowed).
 If ARGS-FOLLOW is non-nil, then options which take arguments may have
 the argument appear after a ganged set of options.  This is how tar
-behaves, for example."
+behaves, for example.
+Arguments NO-GANGING and ARGS-FOLLOW are currently ignored."
   (if (and (= pcomplete-index pcomplete-last)
           (string= (pcomplete-arg) "-"))
       (let ((len (length options))
@@ -1084,8 +1022,8 @@ string, use it as the completion stub instead of the default (which is
 the entire text of the current argument).
 
 For an example of when you might want to use STUB: if the current
-argument text is 'long-path-name/', you don't want the completions
-list display to be cluttered by 'long-path-name/' appearing at the
+argument text is `long-path-name/', you don't want the completions
+list display to be cluttered by `long-path-name/' appearing at the
 beginning of every alternative.  Not only does this make things less
 intelligible, but it is also inefficient.  Yet, if the completion list
 does not begin with this string for every entry, the current argument
@@ -1151,7 +1089,7 @@ Typing SPC flushes the help buffer."
     (setq pcomplete-last-window-config (current-window-configuration)))
   (with-output-to-temp-buffer "*Completions*"
     (display-completion-list completions))
-  (message "Hit space to flush")
+  (minibuffer-message "Hit space to flush")
   (let (event)
     (prog1
         (catch 'done
@@ -1174,7 +1112,7 @@ Typing SPC flushes the help buffer."
                     (scroll-up))))
               (message ""))
              (t
-              (setq unread-command-events (list event))
+              (push event unread-command-events)
               (throw 'done nil)))))
       (if (and pcomplete-last-window-config
                pcomplete-restore-window-delay)
@@ -1191,14 +1129,14 @@ Returns non-nil if a space was appended at the end."
     (if (not pcomplete-ignore-case)
        (insert-and-inherit (if raw-p
                                (substring entry (length stub))
-                             (pcomplete-quote-argument
+                             (comint-quote-filename
                               (substring entry (length stub)))))
       ;; the stub is not quoted at this time, so to determine the
       ;; length of what should be in the buffer, we must quote it
       ;; FIXME: Here we presume that quoting `stub' gives us the exact
       ;; text in the buffer before point, which is not guaranteed;
       ;; e.g. it is not the case in eshell when completing ${FOO}tm[TAB].
-      (delete-backward-char (length (pcomplete-quote-argument stub)))
+      (delete-char (- (length (comint-quote-filename stub))))
       ;; if there is already a backslash present to handle the first
       ;; character, don't bother quoting it
       (when (eq (char-before) ?\\)
@@ -1206,7 +1144,7 @@ Returns non-nil if a space was appended at the end."
        (setq entry (substring entry 1)))
       (insert-and-inherit (if raw-p
                              entry
-                           (pcomplete-quote-argument entry))))
+                           (comint-quote-filename entry))))
     (let (space-added)
       (when (and (not (memq (char-before) pcomplete-suffix-list))
                 addsuffix)
@@ -1216,7 +1154,7 @@ Returns non-nil if a space was appended at the end."
            pcomplete-last-completion-stub stub)
       space-added)))
 
-;; selection of completions
+;; Selection of completions.
 
 (defun pcomplete-do-complete (stub completions)
   "Dynamically complete at point using STUB and COMPLETIONS.
@@ -1240,11 +1178,12 @@ extra checking, and munging of the COMPLETIONS list."
       (setq completions
             (apply-partially 'completion-table-with-predicate
                              completions
-                             (lambda (f)
-                               (not (member
-                                     (funcall pcomplete-norm-func
-                                              (directory-file-name f))
-                                     pcomplete-seen)))
+                             (when pcomplete-seen
+                               (lambda (f)
+                                 (not (member
+                                       (funcall pcomplete-norm-func
+                                                (directory-file-name f))
+                                       pcomplete-seen))))
                              'strict)))
     ;; OK, we've got a list of completions.
     (if pcomplete-show-list
@@ -1330,25 +1269,6 @@ If specific documentation can't be given, be generic."
 
 ;; general utilities
 
-(defun pcomplete-pare-list (l r &optional pred)
-  "Destructively remove from list L all elements matching any in list R.
-Test is done using `equal'.
-If PRED is non-nil, it is a function used for further removal.
-Returns the resultant list."
-  (while (and l (or (and r (member (car l) r))
-                   (and pred
-                        (funcall pred (car l)))))
-    (setq l (cdr l)))
-  (let ((m l))
-    (while m
-      (while (and (cdr m)
-                 (or (and r (member (cadr m) r))
-                     (and pred
-                          (funcall pred (cadr m)))))
-       (setcdr m (cddr m)))
-      (setq m (cdr m))))
-  l)
-
 (defun pcomplete-uniqify-list (l)
   "Sort and remove multiples in L."
   (setq l (sort l 'string-lessp))
@@ -1382,5 +1302,4 @@ Returns the resultant list."
 
 (provide 'pcomplete)
 
-;; arch-tag: ae32ef2d-dbed-4244-8b0f-cf5a2a3b07a4
 ;;; pcomplete.el ends here