]> code.delx.au - gnu-emacs/blobdiff - lisp/pcomplete.el
Make autoloading commands prompt for autoload file (Bug#7989)
[gnu-emacs] / lisp / pcomplete.el
index ae2ef4b49edd84ba365b5bc215a59754ff18a8f5..2f5dcdfb5e8f5028b3c2a51f8185461fa7dd54f1 100644 (file)
@@ -1,7 +1,6 @@
 ;;; pcomplete.el --- programmable completion
 
-;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
-;;   2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+;; Copyright (C) 1999-2011 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
 ;; Keywords: processes abbrev
   :group 'pcomplete)
 
 (defcustom pcomplete-ignore-case (memq system-type '(ms-dos windows-nt cygwin))
+  ;; FIXME: the doc mentions file-name completion, but the code
+  ;; seems to apply it to all completions.
   "If non-nil, ignore case when doing filename completion."
   :type 'boolean
   :group 'pcomplete)
@@ -347,8 +348,209 @@ 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.
+
+;; The way pcomplete-parse-arguments, pcomplete-stub, and
+;; pcomplete-quote-argument work only works because of some deep
+;; hypothesis about the way the completion work.  Basically, it makes
+;; 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.
+
+(defvar pcomplete-unquote-argument-function nil)
+
+(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))))
+
+(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))
+    (- (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-completions-at-point ()
+  "Provide standard completion using pcomplete's completion tables.
+Same as `pcomplete' but using the standard completion UI."
+  ;; 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
+           pcomplete-args pcomplete-last pcomplete-index
+           (pcomplete-autolist pcomplete-autolist)
+           (pcomplete-suffix-list pcomplete-suffix-list)
+           ;; Apparently the vars above are global vars modified by
+           ;; side-effects, whereas pcomplete-completions is the core
+           ;; function that finds the chunk of text to complete
+           ;; (returned indirectly in pcomplete-stub) and the set of
+           ;; possible completions.
+           (completions (pcomplete-completions))
+           ;; Usually there's some close connection between pcomplete-stub
+           ;; and the text before point.  But depending on what
+           ;; 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
+           ;; 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))))
+      (when completions
+        (let ((table
+               (cond
+                ((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 pcomplete-use-paring pcomplete-seen)
+                 (setq pcomplete-seen
+                       (mapcar (lambda (f)
+                                 (funcall pcomplete-norm-func
+                                          (directory-file-name f)))
+                               pcomplete-seen))
+                 (lambda (f)
+                   (not (when pcomplete-seen
+                          (member
+                           (funcall pcomplete-norm-func
+                                    (directory-file-name f))
+                           pcomplete-seen)))))))
+          (unless (zerop (length pcomplete-termination-string))
+            ;; 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.
+            (setq table
+                  (apply-partially #'completion-table-with-terminator
+                                   (cons pcomplete-termination-string
+                                         "\\`a\\`")
+                                   table)))
+          (when pcomplete-ignore-case
+            (setq table
+                  (apply-partially #'completion-table-case-fold table)))
+          (list beg (point) table :predicate pred))))))
+
+ ;; 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.
+
 ;;;###autoload
 (defun pcomplete (&optional interactively)
   "Support extensible programmable completion.
@@ -362,12 +564,11 @@ 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
-             (setq pcomplete-current-completions
-                   (cons (car (last pcomplete-current-completions))
-                         pcomplete-current-completions))
+              (push (car (last pcomplete-current-completions))
+                    pcomplete-current-completions)
              (setcdr (last pcomplete-current-completions 2) nil))
          (nconc pcomplete-current-completions
                 (list (car pcomplete-current-completions)))
@@ -394,41 +595,6 @@ completion functions list (it should occur fairly early in the list)."
                                           '(sole shortest))
                                     pcomplete-last-completion-raw))))))
 
-(defun pcomplete-std-complete ()
-  "Provide standard completion using pcomplete's completion tables.
-Same as `pcomplete' but using the standard completion UI."
-  (interactive)
-  ;; FIXME: it fails to unquote/requote the arguments.
-  ;; FIXME: it doesn't implement paring.
-  ;; FIXME: when we bring up *Completions* we never bring it back down.
-  (catch 'pcompleted
-    (let* ((pcomplete-stub)
-           pcomplete-seen pcomplete-norm-func
-           pcomplete-args pcomplete-last pcomplete-index
-           (pcomplete-autolist pcomplete-autolist)
-           (pcomplete-suffix-list pcomplete-suffix-list)
-           ;; Apparently the vars above are global vars modified by
-           ;; side-effects, whereas pcomplete-completions is the core
-           ;; function that finds the chunk of text to complete
-           ;; (returned indirectly in pcomplete-stub) and the set of
-           ;; possible completions.
-           (completions (pcomplete-completions))
-           ;; The pcomplete code seems to presume that pcomplete-stub
-           ;; is always the text before point.
-           (ol (make-overlay (- (point) (length pcomplete-stub))
-                             (point) nil nil t))
-           (minibuffer-completion-table
-            ;; 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.
-            (apply-partially 'completion-table-with-terminator
-                             '(" " . "\\`a\\`") completions))
-           (minibuffer-completion-predicate nil))
-      (overlay-put ol 'field 'pcomplete)
-      (unwind-protect
-          (call-interactively 'minibuffer-complete)
-        (delete-overlay ol)))))
-
 ;;;###autoload
 (defun pcomplete-reverse ()
   "If cycling completion is in use, cycle backwards."
@@ -461,7 +627,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
@@ -484,7 +650,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))
@@ -493,17 +659,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
@@ -610,12 +765,12 @@ user actually typed in."
       (goto-char begin)
       (while (< (point) end)
        (skip-chars-forward " \t\n")
-       (setq begins (cons (point) begins))
+       (push (point) begins)
        (skip-chars-forward "^ \t\n")
-       (setq args (cons (buffer-substring-no-properties
-                         (car begins) (point))
-                        args)))
-      (cons (reverse args) (reverse begins)))))
+       (push (buffer-substring-no-properties
+               (car begins) (point))
+              args))
+      (cons (nreverse args) (nreverse begins)))))
 
 ;;;###autoload
 (defun pcomplete-comint-setup (completef-sym)
@@ -625,10 +780,13 @@ 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)
-  (make-local-variable completef-sym)
+  (set (make-local-variable completef-sym)
+       (copy-sequence (symbol-value completef-sym)))
   (let* ((funs (symbol-value completef-sym))
-        (elem (or (memq 'comint-dynamic-complete-filename funs)
-                  (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)
       (add-to-list completef-sym 'pcomplete))))
@@ -636,7 +794,8 @@ this is `comint-dynamic-complete-functions'."
 ;;;###autoload
 (defun pcomplete-shell-setup ()
   "Setup `shell-mode' to use pcomplete."
-  (pcomplete-comint-setup 'shell-dynamic-complete-functions))
+  ;; FIXME: insufficient
+  (pcomplete-comint-setup 'comint-dynamic-complete-functions))
 
 (declare-function comint-bol "comint" (&optional arg))
 
@@ -649,17 +808,16 @@ this is `comint-dynamic-complete-functions'."
       (goto-char begin)
       (while (< (point) end)
        (skip-chars-forward " \t\n")
-       (setq begins (cons (point) begins))
+       (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))))
-       (setq args (cons (buffer-substring-no-properties
-                         (car begins) (point))
-                        args)))
-      (cons (reverse args) (reverse begins)))))
+       (push (buffer-substring-no-properties (car begins) (point))
+              args))
+      (cons (nreverse args) (nreverse begins)))))
 
 (defun pcomplete-parse-arguments (&optional expand-p)
   "Parse the command line arguments.  Most completions need this info."
@@ -672,9 +830,9 @@ this is `comint-dynamic-complete-functions'."
            pcomplete-stub (pcomplete-arg 'last))
       (let ((begin (pcomplete-begin 'last)))
        (if (and pcomplete-cycle-completions
-                (listp pcomplete-stub)
+                (listp pcomplete-stub) ;??
                 (not pcomplete-expand-only-p))
-           (let* ((completions pcomplete-stub)
+           (let* ((completions pcomplete-stub) ;??
                   (common-stub (car completions))
                   (c completions)
                   (len (length common-stub)))
@@ -713,23 +871,17 @@ this is `comint-dynamic-complete-functions'."
 Magic characters are those in `pcomplete-arg-quote-list'."
   (if (null pcomplete-arg-quote-list)
       filename
-    (let ((len (length filename))
-         (index 0)
-         (result "")
-         replacement char)
-      (while (< index len)
-       (setq replacement (run-hook-with-args-until-success
-                          'pcomplete-quote-arg-hook filename index))
-       (cond
-        (replacement
-         (setq result (concat result replacement)))
-        ((and (setq char (aref filename index))
-              (memq char pcomplete-arg-quote-list))
-         (setq result (concat result "\\" (char-to-string char))))
-        (t
-         (setq result (concat result (char-to-string char)))))
-       (setq index (1+ index)))
-      result)))
+    (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
+                 ""))))
 
 ;; file-system completion lists
 
@@ -846,13 +998,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))
@@ -864,7 +1017,7 @@ behaves, for example."
              (let ((result (read-from-string options index)))
                (setq index (cdr result)))
            (unless (memq char '(?/ ?* ?? ?.))
-             (setq choices (cons (char-to-string char) choices)))
+             (push (char-to-string char) choices))
            (setq index (1+ index))))
        (throw 'pcomplete-completions
               (mapcar
@@ -912,11 +1065,10 @@ See the documentation for `pcomplete-here'."
          (unless (eq paring t)
            (let ((arg (pcomplete-arg)))
              (when (stringp arg)
-               (setq pcomplete-seen
-                     (cons (if paring
-                               (funcall paring arg)
-                             (file-truename arg))
-                           pcomplete-seen))))))
+                (push (if paring
+                          (funcall paring arg)
+                        (file-truename arg))
+                      pcomplete-seen)))))
        (pcomplete-next-arg)
        t)
     (when pcomplete-show-help
@@ -973,7 +1125,7 @@ generate the completions list.  This means that the hook
 (defmacro pcomplete-here* (&optional form stub form-only)
   "An alternate form which does not participate in argument paring."
   (declare (debug t))
-  `(pcomplete-here (lambda () ,form) ,stub t ,form-only))
+  `(pcomplete-here ,form ,stub t ,form-only))
 
 ;; display support
 
@@ -1055,7 +1207,10 @@ Returns non-nil if a space was appended at the end."
                               (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
-      (delete-backward-char (length (pcomplete-quote-argument stub)))
+      ;; 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-char (- (length (pcomplete-quote-argument stub))))
       ;; if there is already a backslash present to handle the first
       ;; character, don't bother quoting it
       (when (eq (char-before) ?\\)
@@ -1097,11 +1252,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
@@ -1239,5 +1395,4 @@ Returns the resultant list."
 
 (provide 'pcomplete)
 
-;; arch-tag: ae32ef2d-dbed-4244-8b0f-cf5a2a3b07a4
 ;;; pcomplete.el ends here