]> code.delx.au - gnu-emacs/blobdiff - lisp/complete.el
Update GPL version for Emacs images.
[gnu-emacs] / lisp / complete.el
index 90c1ceceb3224b975f43f008cbe5bb2edf3f214b..4b4e4784b3f33bf7a8ae57ff1cde358ab5388905 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, 2006 Free Software Foundation, Inc.
+;; Copyright (C) 1990, 1991, 1992, 1993, 1999, 2000, 2001, 2002, 2003,
+;;   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author: Dave Gillespie <daveg@synaptics.com>
 ;; Keywords: abbrev convenience
@@ -11,7 +11,7 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -147,6 +147,8 @@ If nil, means use the colon-separated path in the variable $INCPATH instead."
     (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)
@@ -157,8 +159,10 @@ If nil, means use the colon-separated path in the variable $INCPATH instead."
           (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)
@@ -181,7 +185,18 @@ If nil, means use the colon-separated path in the variable $INCPATH instead."
           (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
@@ -235,7 +250,16 @@ second TAB brings up the `*Completions*' buffer."
    (if partial-completion-mode 'add-hook 'remove-hook)
    'choose-completion-string-functions
    (lambda (choice buffer mini-p base-size)
-     (if mini-p (goto-char (point-max)))
+     ;; 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)
+              mini-p)
+         (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))
@@ -383,14 +407,46 @@ of `minibuffer-completion-table' and the minibuffer contents.")
     (let ((completion-ignore-case nil))
       (test-completion str table pred))))
 
-(defun PC-do-completion (&optional mode beg end)
+;; 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)
         (pred minibuffer-completion-predicate)
         (filename (funcall PC-completion-as-file-name-predicate))
-        (dirname nil)          ; non-nil only if a filename is being completed
-        (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)
@@ -404,8 +460,15 @@ of `minibuffer-completion-table' and the minibuffer contents.")
 
     ;; Check if buffer contents can already be considered complete
     (if (and (eq mode 'exit)
-            (test-completion-ignore-case str table pred))
-       'complete
+            (test-completion str table pred))
+       (progn
+         ;; If completion-ignore-case is non-nil, insert the
+         ;; completion string since that may have a different case.
+         (when completion-ignore-case
+           (setq str (PC-try-completion str table pred))
+           (delete-region beg end)
+           (insert str))
+         'complete)
 
       ;; Do substitutions in directory names
       (and filename
@@ -553,6 +616,9 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                   (setq poss (cons (car p) poss))))
            (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))
@@ -623,8 +689,8 @@ 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 (PC-chunk-after basestr skip)
-                                                  poss)))
+                    (setq prefix (PC-try-completion
+                                  (PC-chunk-after basestr skip) poss)))
                (let ((first t) i)
                  ;; Retain capitalization of user input even if
                  ;; completion-ignore-case is set.
@@ -641,7 +707,7 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                                      (forward-char 1)
                                    (if (and (< (point) end)
                                             (and (looking-at " ")
-                                                     (memq (aref prefix i)
+                                                  (memq (aref prefix i)
                                                        PC-delims-list)))
                                        ;; replace " " by the actual delimiter
                                        (progn
@@ -649,12 +715,12 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                                          (insert (substring prefix i (1+ i))))
                                      ;; insert a new character
                                      (progn
-                                     (and filename (looking-at "\\*")
-                                          (progn
-                                            (delete-char 1)
-                                            (setq end (1- end))))
+                                        (and filename (looking-at "\\*")
+                                             (progn
+                                               (delete-char 1)
+                                               (setq end (1- end))))
                                        (setq improved t)
-                                   (insert (substring prefix i (1+ i)))
+                                        (insert (substring prefix i (1+ i)))
                                        (setq end (1+ end)))))
                                  (setq i (1+ i)))
                                (or pt (setq pt (point)))
@@ -662,7 +728,7 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                              (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
@@ -689,7 +755,7 @@ of `minibuffer-completion-table' and the minibuffer contents.")
 
                    ;; We changed it... would it be complete without the space?
                    (if (test-completion (buffer-substring 1 (1- end))
-                                         table pred)
+                                         table pred)
                        (delete-region (1- end) end)))
 
              (if improved
@@ -703,13 +769,30 @@ of `minibuffer-completion-table' and the minibuffer contents.")
                        (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)))
+                    (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]"))
                nil)))))
 
@@ -759,6 +842,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.
@@ -771,6 +858,12 @@ Otherwise, all symbols with function definitions, values
 or properties are considered."
   (interactive)
   (let* ((end (point))
+         ;; To complete the word under point, rather than just the portion
+         ;; before point, use this:
+;;;           (save-excursion
+;;;             (with-syntax-table lisp-mode-syntax-table
+;;;               (forward-sexp 1)
+;;;               (point))))
         (beg (save-excursion
                 (with-syntax-table lisp-mode-syntax-table
                   (backward-sexp 1)
@@ -785,7 +878,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
+    ;; behaviour) 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 behaviour 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 behaviour 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.
@@ -986,17 +1111,18 @@ absolute rather than relative to some directory on the SEARCH-PATH."
   (if (string-match "<\\([^\"<>]*\\)>?\\'" (ad-get-arg 0))
       (let* ((string (ad-get-arg 0))
              (action (ad-get-arg 2))
-             (name (substring string (match-beginning 1) (match-end 1)))
+             (name (match-string 1 string))
             (str2 (substring string (match-beginning 0)))
             (completion-table
-             (mapcar (lambda (x) (format "<%s>" x))
+             (mapcar (lambda (x)
+                        (format (if (string-match "/\\'" x) "<%s" "<%s>") x))
                      (PC-include-file-all-completions
                       name (PC-include-file-path)))))
         (setq ad-return-value
               (cond
                ((not completion-table) nil)
                ((eq action 'lambda) (test-completion str2 completion-table nil))
-               ((eq action nil) (try-completion str2 completion-table nil))
+               ((eq action nil) (PC-try-completion str2 completion-table nil))
                ((eq action t) (all-completions str2 completion-table nil)))))
     ad-do-it))
 \f