Package: emacs;
Reported by: João Távora <joaotavora <at> gmail.com>
Date: Thu, 17 Jan 2019 13:57:02 UTC
Severity: normal
Tags: patch
Found in version 27.0.50
Done: João Távora <joaotavora <at> gmail.com>
Bug is archived. No further changes may be made.
View this message in rfc822 format
From: João Távora <joaotavora <at> gmail.com> To: 34116 <at> debbugs.gnu.org Cc: monnier <at> iro.umontreal.ca Subject: bug#34116: 27.0.50; minibuffer-force-complete-and-exit mostly broken Date: Thu, 17 Jan 2019 13:56:39 +0000
[Message part 1 (text/plain, inline)]
Hi maintainers, Here's a cute one from my recent icomplete adventures, Emacs -Q M-x icomplete-mode M-: (completing-read "test: " '("foo" "bar")) C-M-i ;; minibuffer-force-complete, this completes to "foo" C-j ;; icomplete-force-complete-and-exit Expected "foo", right? Nope: you get "bar". Incidently, this also happens without icomplete, provided you have bound the commands minibuffer-force-complete and minibuffer-force-complete-and-exit in minibuffer-local-completion-map. The problem happens because m-f-b cycles/rotates silently rotates the candidate list _after_ forcing the completion. The other second "and-exit" command, which also calls m-f-b during its execution, catches this extra rotation before returning the final value to the user. The attached patch fixes this. It add considerable hackery to an already nasty collection of hacks, but is the safest way short of redesigning this whole cycling business (which is heavily used in completion-at-point). This patch also simplifies the fix for bug#34077 which I had previously submitted. João
[0001-Fix-nasty-cycling-on-minibuffer-force-complete-and-e.patch (text/x-patch, inline)]
From 97756513d4fe4521f3eff5376ad1ecfce5e1f266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= <joaotavora <at> gmail.com> Date: Wed, 16 Jan 2019 23:29:34 +0000 Subject: [PATCH] Fix nasty cycling on minibuffer-force-complete-and-exit minibuffer-force-complete sets up cycling after forcing the completion, which is mostly fine for successive interactive calls. However minibuffer-force-complete-and-exit also calls it. In situations where the former and latter are called in succession this causes an unwanted extra cycle, which ultimately yields the wrong completion. * lisp/minibuffer.el (minibuffer-force-complete): Take DONT-CYCLE arg. (minibuffer-force-complete-and-exit): Pass DONT-CYCLE to minibuffer-force-complete. --- lisp/minibuffer.el | 79 +++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5760a2e49d..74f85e7b90 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -1257,29 +1257,32 @@ completion-all-sorted-completions (defun minibuffer-force-complete-and-exit () "Complete the minibuffer with first of the matches and exit." (interactive) - (minibuffer-force-complete) + (minibuffer-force-complete nil nil t) (completion--complete-and-exit (minibuffer-prompt-end) (point-max) #'exit-minibuffer ;; If the previous completion completed to an element which fails ;; test-completion, then we shouldn't exit, but that should be rare. (lambda () (minibuffer-message "Incomplete")))) -(defun minibuffer-force-complete (&optional start end) - "Complete the minibuffer to an exact match. -Repeated uses step through the possible completions." +(defun minibuffer-force-complete (&optional start end dont-cycle) + "Complete the minibuffer string between START and END to an exact match. +Repeated uses step through the possible completions, unless +prevented to do so by DONT-CYCLE." (interactive) (setq minibuffer-scroll-window nil) ;; FIXME: Need to deal with the extra-size issue here as well. ;; FIXME: ~/src/emacs/t<M-TAB>/lisp/minibuffer.el completes to ;; ~/src/emacs/trunk/ and throws away lisp/minibuffer.el. + ;; FIXME: shouldn't we cycle _before_ instead of after forcing?? (let* ((start (copy-marker (or start (minibuffer-prompt-end)))) (end (or end (point-max))) ;; (md (completion--field-metadata start)) (all (completion-all-sorted-completions start end)) - (base (+ start (or (cdr (last all)) 0)))) + (base (+ start (or (cdr (last all)) 0))) + last second-last) (cond ((not (consp all)) - (completion--message + (completion--message (if all "No more completions" "No completions"))) ((not (consp (cdr all))) (let ((done (equal (car all) (buffer-substring-no-properties base end)))) @@ -1287,36 +1290,48 @@ minibuffer-force-complete (completion--done (buffer-substring-no-properties start (point)) 'finished (when done "Sole completion")))) (t + (setq second-last (last all 2) + last (cdr second-last)) + ;; If we happened to be already cycling, we must undo the + ;; effects of the last rotation (get out yer' pen and paper to + ;; get the cons cell math). + (when (and dont-cycle completion-cycling) + (let ((lastcdr (cddr second-last))) + (setcdr (cdr second-last) all) + (setq all (cdr second-last)) + (setcdr second-last lastcdr) + (setq last second-last))) (completion--replace base end (car all)) (setq end (+ base (length (car all)))) (completion--done (buffer-substring-no-properties start (point)) 'sole) - ;; Set cycling after modifying the buffer since the flush hook resets it. - (setq completion-cycling t) - (setq this-command 'completion-at-point) ;For completion-in-region. - ;; If completing file names, (car all) may be a directory, so we'd now - ;; have a new set of possible completions and might want to reset - ;; completion-all-sorted-completions to nil, but we prefer not to, - ;; so that repeated calls minibuffer-force-complete still cycle - ;; through the previous possible completions. - (let ((last (last all))) + (unless dont-cycle + ;; Set cycling after modifying the buffer since the flush hook resets it. + (setq completion-cycling t) + (setq this-command 'completion-at-point) ;For completion-in-region. + ;; Rotate the completions collected earlier and cache them. If + ;; completing file names, (car all) may be a directory, so we'd + ;; now have a new set of possible completions and might want to + ;; reset completion-all-sorted-completions to nil, but we prefer + ;; not to, so that repeated calls minibuffer-force-complete + ;; still cycle through the previous possible completions. (setcdr last (cons (car all) (cdr last))) - (completion--cache-all-sorted-completions start end (cdr all))) - ;; Make sure repeated uses cycle, even though completion--done might - ;; have added a space or something that moved us outside of the field. - ;; (bug#12221). - (let* ((table minibuffer-completion-table) - (pred minibuffer-completion-predicate) - (extra-prop completion-extra-properties) - (cmd - (lambda () "Cycle through the possible completions." - (interactive) - (let ((completion-extra-properties extra-prop)) - (completion-in-region start (point) table pred))))) - (set-transient-map - (let ((map (make-sparse-keymap))) - (define-key map [remap completion-at-point] cmd) - (define-key map (vector last-command-event) cmd) - map))))))) + (completion--cache-all-sorted-completions start end (cdr all)) + ;; Make sure repeated uses cycle, even though completion--done + ;; might have added a space or something that moved us outside + ;; of the field. (bug#12221). + (let* ((table minibuffer-completion-table) + (pred minibuffer-completion-predicate) + (extra-prop completion-extra-properties) + (cmd + (lambda () "Cycle through the possible completions." + (interactive) + (let ((completion-extra-properties extra-prop)) + (completion-in-region start (point) table pred))))) + (set-transient-map + (let ((map (make-sparse-keymap))) + (define-key map [remap completion-at-point] cmd) + (define-key map (vector last-command-event) cmd) + map)))))))) (defvar minibuffer-confirm-exit-commands '(completion-at-point minibuffer-complete -- 2.19.2
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.