emacs-pretest-bug
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Partial completion (complete.el) fails to respect case of user input


From: Michael Ernst
Subject: Partial completion (complete.el) fails to respect case of user input
Date: Mon, 12 Jun 2006 12:09:12 -0400

Ordinary minibuffer completion preserves case when possible, even when
completion-ignore-case is set.  Partial completion (from complete.el) does
not.  As a result, partial completion can return a different result than
normal completion, when completion-ignore-case is set.  Normal completion
gives the more intuitive result, and partial completion should do likewise,
for both usability and consistency.

I have attached below some tests, plus a patch that corrects the problems.

                    -Michael Ernst
                     address@hidden


;;; As preparation, define procedures.

(defun completion-test-1 (initial-input &optional ignore-case)
  (let ((completion-ignore-case ignore-case))
    (completing-read "Input: "
                     '(("iterator" . 1)
                       ("Iterator" . 2)
                       ("ITERATOR" . 3)
                       ("foo" . 4))
                     nil                ; no predicate, all answers acceptable
                     t                  ; require match
                     initial-input
                     )))
(defun completion-test-2 (initial-input &optional ignore-case)
  (let ((completion-ignore-case ignore-case))
    (completing-read "Input: "
                     '(("IOException" . 1)
                       ("ioException" . 2)
                       )
                     nil                ; no predicate, all answers acceptable
                     t                  ; require match
                     initial-input
                     )))
(defun completion-test-3 (initial-input &optional ignore-case)
  (let ((completion-ignore-case ignore-case))
    (completing-read "Input: "
                     '(("hasMaxValue" . 1)
                       ("hasMore" . 2)
                       ("hash" . 3)
                       ("hashCode" . 4)
                       ("HashTable" . 5)
                       ("HashMap" . 6))
                     nil                ; no predicate, all answers acceptable
                     t                  ; require match
                     initial-input
                     )))

;;; Also as preparation, set variables.

(partial-completion-mode 1)
(setq PC-meta-flag t)

;;; Finally, calls to the procedures illustrate the problematic behavior.

(completion-test-1 "Itera" t)
;; user types:  M-RET    [normal completion]
;; result:  Iterator
;; user types:  RET      [partial completion]
;; result:  iterator     [problem:  lower-case "i"]

(completion-test-1 "ITERA" t)
;; user types:  M-RET    [normal completion]
;; result:  ITERATOR
;; user types:  RET      [partial completion]
;; result:  iterator     [problem:  all lower-case]

(completion-test-2 "ioexception" t)
;; user types:  M-RET    [normal completion]
;; result:  IOException  [arbitrarily chooses the first matching element]
;; user types:  RET      [partial completion]
;; result:  ioexception  [problem:  all lower-case]

;; Other tests to verify the changes to complete.el:
(completion-test-1 "FO" t)
(completion-test-2 "ioex" t)
(completion-test-3 "hashm" t)



2006-04-01  Michael Ernst  <address@hidden>

        * complete.el (PC-do-completion): Retain capitalization of user
        input, when possible, even if completion-ignore-case is set.



Index: complete.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/complete.el,v
retrieving revision 1.53
diff -u -u -b -r1.53 complete.el
--- complete.el 22 May 2006 18:09:40 -0000      1.53
+++ complete.el 12 Jun 2006 16:06:07 -0000
@@ -196,7 +196,7 @@
 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,
@@ -358,13 +358,36 @@
 The function takes no arguments, and typically looks at the value
 of `minibuffer-completion-table' and the minibuffer contents.")
 
+;; 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 (format "String %s didn't match regexp %s" string 
regexp)))
+       (message message)
+       (error message)))
+  (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))))
+
 (defun PC-do-completion (&optional mode beg 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)
+        (dirname nil)          ; non-nil only if a filename is being completed
         (dirlength 0)
         (str (buffer-substring beg end))
         (incname (and filename (string-match "<\\([^\"<>]*\\)>?$" str)))
@@ -379,7 +402,7 @@
 
     ;; Check if buffer contents can already be considered complete
     (if (and (eq mode 'exit)
-            (test-completion str table pred))
+            (test-completion-ignore-case str table pred))
        'complete
 
       ;; Do substitutions in directory names
@@ -598,35 +621,38 @@
 
            ;; 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 (try-completion (PC-chunk-after basestr skip) 
(mapcar 'list poss))))
                (let ((first t) i)
+                 ;; Retain capitalization of user input even if
+                 ;; completion-ignore-case is set.
                  (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)))
+                                          (eq (downcase (aref prefix i))
+                                              (downcase (following-char))))
+                                     ;; same char (modulo case); no action
                                      (forward-char 1)
                                    (if (and (< (point) end)
-                                            (or (and (looking-at " ")
+                                            (and (looking-at " ")
                                                      (memq (aref prefix i)
-                                                           PC-delims-list))
-                                                (eq (downcase (aref prefix i))
-                                                    (downcase
-                                                     (following-char)))))
+                                                       PC-delims-list)))
+                                       ;; replace " " by the actual delimiter
                                        (progn
                                          (delete-char 1)
-                                         (setq end (1- end)))
+                                         (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))
+                                       (setq improved t)
                                    (insert (substring prefix i (1+ i)))
-                                   (setq end (1+ end)))
+                                       (setq end (1+ end)))))
                                  (setq i (1+ i)))
                                (or pt (setq pt (point)))
                                (looking-at PC-delim-regex))
@@ -634,7 +660,12 @@
                                                 (regexp-quote prefix)
                                                 PC-ndelims-regex)
                                    prefix (try-completion
-                                           ""
+                                           (PC-chunk-after
+                                            ;; not basestr, because that does
+                                            ;; not reflect insertions
+                                            (buffer-substring
+                                             (+ beg (length dirname)) end)
+                                            skip)
                                            (mapcar
                                             (function
                                              (lambda (x)
@@ -666,7 +697,7 @@
 
                  ;; 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)
@@ -950,11 +981,10 @@
   (if (string-match "<\\([^\"<>]*\\)>?\\'" (ad-get-arg 0))
       (let* ((string (ad-get-arg 0))
              (action (ad-get-arg 2))
-             (name (match-string 1 string))
+             (name (substring string (match-beginning 1) (match-end 1)))
             (str2 (substring string (match-beginning 0)))
             (completion-table
-             (mapcar (lambda (x)
-                       (format (if (string-match "/\\'" x) "<%s" "<%s>") x))
+             (mapcar (lambda (x) (format "<%s>" x))
                      (PC-include-file-all-completions
                       name (PC-include-file-path)))))
         (setq ad-return-value




reply via email to

[Prev in Thread] Current Thread [Next in Thread]