Package: emacs;
Reported by: Daniel Colascione <dancol <at> dancol.org>
Date: Thu, 8 May 2025 22:41:01 UTC
Severity: normal
Found in version 31.0.50
Done: Eli Zaretskii <eliz <at> gnu.org>
Message #27 received at 78323 <at> debbugs.gnu.org (full text, mbox):
From: Spencer Baugh <sbaugh <at> janestreet.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: dancol <at> dancol.org, Stefan Monnier <monnier <at> iro.umontreal.ca>, 78323 <at> debbugs.gnu.org Subject: Re: bug#78323: Regression in case-insensitive filename completion Date: Tue, 13 May 2025 12:23:36 -0400
[Message part 1 (text/plain, inline)]
Eli Zaretskii <eliz <at> gnu.org> writes: >> From: Stefan Monnier <monnier <at> iro.umontreal.ca> >> Cc: Daniel Colascione <dancol <at> dancol.org>, sbaugh <at> janestreet.com, >> 78323 <at> debbugs.gnu.org >> Date: Mon, 12 May 2025 10:42:47 -0400 >> >> >> >> With read-file-name-completion-ignore-case true, commit >> >> >> 509cbe1c35b3dd005a53ac041f9c87ee53b8e115 breaks existing completion >> >> >> behavior. Suppose we have file named fooBarQux. Previously, if I'd >> >> >> typed "foobar" and TAB while reading a filename, then the completion >> >> >> would be "fooBarQux". Now, it completes as "foobarQux", which is wrong >> >> >> and breaks, among other things, vc. >> >> > Spencer, could you please look into fixing this regression? >> >> What do you think of reverting this change while Spencer finds the cause >> >> of the problem? >> > I wouldn't mind, but then I don't use these features. Let's hear >> > Stefan's opinion first. >> >> I'd first like to hear Spencer's opinion on how quickly he might be able to >> fix it or if it indeed breaks his assumptions too hard (so we'd need >> a different approach, in which case we may as well revert the change first). > > So, Spencer, the eyes of all the world are upon you. Sorry for the delay, I was away this weekend. Thanks for your patience. The attached patch should fix the issue; it's what I have installed at my site.
[0001-Fix-completion-ignore-case-with-completion-file-name.patch (text/x-patch, inline)]
From 2418e67a71494396e8dbfd7fd5eabfb52a87198b Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh <at> janestreet.com> Date: Thu, 1 May 2025 13:56:37 -0400 Subject: [PATCH] Fix completion-ignore-case with completion--file-name-table 509cbe1c35b3d "Improve env var handling in read-file-name" caused try-completion and all-completion operations with completion--file-name-table to no longer update the case of text which was already present in the input string. That is, completions would be returned ignoring case, but the completions would have letter-casing which matched the input string rather than matching the actual file names. Fix this by always using text from the file name completions whenever it might have changed case. Also, fix a related bug where mixing completion-ignore-case and expanding environment variables would cause `read-file-name' to return the wrong result; do this by suppressing completion-ignore-case in the problematic case. * lisp/minibuffer.el (completion--file-name-table): Use text from the completions, not the input string. (bug#78323) * test/lisp/minibuffer-tests.el (completion-table-test-quoting): Test with completion-ignore-case as well. --- lisp/minibuffer.el | 28 +++++++++++++++++++--------- test/lisp/minibuffer-tests.el | 16 +++++++++++++++- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 7b2b986aa1d..6f9e6c67541 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -3597,6 +3597,16 @@ completion--file-name-table (if (eq (car-safe action) 'boundaries) (cons 'boundaries (completion--sifn-boundaries orig table pred (cdr action))) (let* ((sifned (substitute-in-file-name orig)) + (orig-start (car (completion--sifn-boundaries orig table pred ""))) + (sifned-start (car (completion-boundaries sifned table pred ""))) + (orig-in-bounds (substring orig orig-start)) + (sifned-in-bounds (substring sifned sifned-start)) + (only-need-double-dollars + ;; If true, sifn only un-doubled $s in ORIG, so we can fix a + ;; completion to match ORIG by just doubling $s again. This + ;; preserves more text from the completion, behaving better with + ;; non-nil `completion-ignore-case'. + (string-equal orig-in-bounds (minibuffer--double-dollars sifned-in-bounds))) (result (let ((completion-regexp-list ;; Regexps are matched against the real file names after @@ -3611,21 +3621,21 @@ completion--file-name-table (if (stringp result) ;; Extract the newly added text, quote any dollar signs, and ;; append it to ORIG. - (let ((new-text (substring result (length sifned)))) - (concat orig (minibuffer--double-dollars new-text))) + (if only-need-double-dollars + (concat (substring orig nil orig-start) + (minibuffer--double-dollars (substring result sifned-start))) + (let ((new-text (substring result (length sifned)))) + (concat orig (minibuffer--double-dollars new-text)))) result)) ((eq action t) ; all-completions (mapcar - (let ((orig-prefix - (substring orig (car (completion--sifn-boundaries orig table pred "")))) - (sifned-prefix-length - (- (length sifned) - (car (completion-boundaries sifned table pred ""))))) + (if only-need-double-dollars + #'minibuffer--double-dollars ;; Extract the newly added text, quote any dollar signs, and append ;; it to the part of ORIG inside the completion boundaries. (lambda (compl) - (let ((new-text (substring compl sifned-prefix-length))) - (concat orig-prefix (minibuffer--double-dollars new-text))))) + (let ((new-text (substring compl (length sifned-in-bounds)))) + (concat orig-in-bounds (minibuffer--double-dollars new-text))))) result)) (t result)))))) diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el index bed797bdb14..111335a2b5e 100644 --- a/test/lisp/minibuffer-tests.el +++ b/test/lisp/minibuffer-tests.el @@ -108,7 +108,21 @@ completion-table-test-quoting (should (equal (completion-try-completion input #'completion--file-name-table nil (length input)) - (cons output (length output))))))) + (cons output (length output))))) + ;; Everything also works with `completion-ignore-case'. + (let ((completion-ignore-case t)) + (pcase-dolist (`(,input ,output) + '( + ("data/M-CTTQ" "data/minibuffer-test-cttq$$tion") + ("data/M-CTTQ$$t" "data/minibuffer-test-cttq$$tion") + ("lisp/c${CTTQ1}et/SE-U" "lisp/c${CTTQ1}et/semantic-utest") + ("lisp/ced${CTTQ2}SE-U" "lisp/ced${CTTQ2}SEmantic-utest") + ("lis/c${CTTQ1}/SE-U" "lisp/c${CTTQ1}et/semantic-utest") + )) + (should (equal (car (completion-try-completion input + #'completion--file-name-table + nil (length input))) + output)))))) (ert-deftest completion--insert-strings-faces () (with-temp-buffer -- 2.39.3
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.