;;; 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
;; 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,
(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 "\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)
(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
(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))
(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)
;; 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
(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))
;; 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.
(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
(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)))
(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
;; 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
(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)))))
(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.
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)
(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.
(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