* Navigating completions from minibuffer @ 2023-11-07 3:57 T.V Raman 2023-11-07 4:55 ` T.V Raman 0 siblings, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-07 3:57 UTC (permalink / raw) To: emacs-devel This change: f0c0ff6bf23 * New option to use arrows in the minibuffer to select completions (bug#59486) works really well but appears to introduce a separate problem: Assume you have a command e.g. foo but also foo-bar and foo-bas. Earlier you could type M-x foo and press enter to invoke foo. Now, with the new option enabled, typing M-x foo appears to require that you first press TAB before pressing RET. -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Navigating completions from minibuffer 2023-11-07 3:57 Navigating completions from minibuffer T.V Raman @ 2023-11-07 4:55 ` T.V Raman 2023-11-07 7:20 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-07 4:55 UTC (permalink / raw) To: raman; +Cc: emacs-devel Following up to myself: The problem is that now RET calls minibuffer-choose-completion, whereas exit-minibuffer is what you want if what you have typed is a valid completion already; Perhaps we need minibuffer-choose-completion-or-exit-minibuffer ... T.V Raman writes: > This change: > f0c0ff6bf23 * New option to use arrows in the minibuffer to select completions (bug#59486) > works really well but appears to introduce a separate problem: > > Assume you have a command e.g. foo but also foo-bar and foo-bas. > > Earlier you could type M-x foo and press enter to invoke foo. > > Now, with the new option enabled, typing > M-x foo > appears to require that you first press TAB before pressing RET. > > -- -- -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-07 4:55 ` T.V Raman @ 2023-11-07 7:20 ` Juri Linkov 2023-11-07 17:53 ` T.V Raman 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-07 7:20 UTC (permalink / raw) To: T.V Raman; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 800 bytes --] >> This change: >> f0c0ff6bf23 * New option to use arrows in the minibuffer to select completions (bug#59486) >> works really well but appears to introduce a separate problem: >> >> Assume you have a command e.g. foo but also foo-bar and foo-bas. >> >> Earlier you could type M-x foo and press enter to invoke foo. >> >> Now, with the new option enabled, typing >> M-x foo >> appears to require that you first press TAB before pressing RET. > > The problem is that now RET calls minibuffer-choose-completion, > whereas exit-minibuffer is what you want if what you have typed is a > valid completion already; Perhaps we need > minibuffer-choose-completion-or-exit-minibuffer ... Agreed. When a completion candidate is not selected let's use the contents of the minibuffer. Please try this patch: [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: minibuffer-choose-completion-or-exit.patch --] [-- Type: text/x-diff, Size: 1223 bytes --] diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 4e8590989b3..0410e3a0b8d 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -3005,7 +3005,7 @@ minibuffer-visible-completions-map "<right>" (minibuffer-visible-completions-bind #'minibuffer-next-completion) "<up>" (minibuffer-visible-completions-bind #'minibuffer-previous-line-completion) "<down>" (minibuffer-visible-completions-bind #'minibuffer-next-line-completion) - "RET" (minibuffer-visible-completions-bind #'minibuffer-choose-completion) + "RET" (minibuffer-visible-completions-bind #'minibuffer-choose-completion-or-exit) "C-g" (minibuffer-visible-completions-bind #'minibuffer-hide-completions)) \f @@ -4693,6 +4693,12 @@ minibuffer-choose-completion (let ((completion-use-base-affixes t)) (choose-completion nil no-exit no-quit)))) +(defun minibuffer-choose-completion-or-exit (&optional no-exit no-quit) + (interactive "P") + (condition-case nil + (minibuffer-choose-completion no-exit no-quit) + (error (exit-minibuffer)))) + (defun minibuffer-complete-history () "Complete the minibuffer history as far as possible. Like `minibuffer-complete' but completes on the history items ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-07 7:20 ` Juri Linkov @ 2023-11-07 17:53 ` T.V Raman 2023-11-07 19:36 ` T.V Raman 0 siblings, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-07 17:53 UTC (permalink / raw) To: juri; +Cc: raman, emacs-devel am very confused. Installed patch, recompiled, and installed -- restarted emacs everything worked as expected. But now my emacs cant find the --or-exit command we just bound; where is it defined? -- -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-07 17:53 ` T.V Raman @ 2023-11-07 19:36 ` T.V Raman 2023-11-08 7:39 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-07 19:36 UTC (permalink / raw) To: juri; +Cc: emacs-devel everything works, now that I actually applied the complete patch:-) -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-07 19:36 ` T.V Raman @ 2023-11-08 7:39 ` Juri Linkov 2023-11-08 16:21 ` T.V Raman 2023-11-10 13:12 ` Spencer Baugh 0 siblings, 2 replies; 62+ messages in thread From: Juri Linkov @ 2023-11-08 7:39 UTC (permalink / raw) To: T.V Raman; +Cc: emacs-devel > everything works, now that I actually applied the complete patch:-) Thanks for confirming and for suggesting this change, now pushed. Probably we have to make RET more smart, so that when more editing was performed in the minibuffer after the completions were displayed, then to use the minibuffer contents with exit-minibuffer, not an obsolete completion candidate that remains selected. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-08 7:39 ` Juri Linkov @ 2023-11-08 16:21 ` T.V Raman 2023-11-08 17:18 ` T.V Raman 2023-11-10 13:12 ` Spencer Baugh 1 sibling, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-08 16:21 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: Agreed. And somewhat related and something that has been causing me trouble: Q: Why is completion-at-point *soo much* slower than hippie-expand? I tried to understand why by looking at the code for completion-at-point but failed miserably. Hope you could look at this since you're working on completion related bits. Also, how and when completions are displayed can now be controlled by multiple custom knobs, but it's hard to map the combinatorial explosion of the available possibilities to different user experiences without trying all possible settings; a higher level overview with a couple of recipes that describe common combinations would help. >> everything works, now that I actually applied the complete patch:-) > > Thanks for confirming and for suggesting this change, now pushed. > > Probably we have to make RET more smart, so that when more editing > was performed in the minibuffer after the completions were displayed, > then to use the minibuffer contents with exit-minibuffer, > not an obsolete completion candidate that remains selected. > -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-08 16:21 ` T.V Raman @ 2023-11-08 17:18 ` T.V Raman 2023-11-08 22:11 ` Slow completion-at-point was " T.V Raman 0 siblings, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-08 17:18 UTC (permalink / raw) To: emacs-devel; +Cc: juri, emacs-devel Adding emacs-devel after verifying slowness: 1. completion-at-point appears to have gotten very slow -- from memory in the last week (emacs built against Git @HEAD) The slowness is not present in a build from October 4. T.V Raman writes: > Juri Linkov <juri@linkov.net> writes: > > > Agreed. And somewhat related and something that has been causing me > trouble: > > Q: Why is completion-at-point *soo much* slower than > hippie-expand? > > I tried to understand why by looking at the code for completion-at-point > but failed miserably. > > Hope you could look at this since you're working on completion related > bits. > > > Also, how and when completions are displayed can now be controlled by > multiple custom knobs, but it's hard to map the combinatorial explosion > of the available possibilities to different user experiences without > trying all possible settings; a higher level overview with a couple of > recipes that describe common combinations would help. > > > >> everything works, now that I actually applied the complete patch:-) > > > > Thanks for confirming and for suggesting this change, now pushed. > > > > Probably we have to make RET more smart, so that when more editing > > was performed in the minibuffer after the completions were displayed, > > then to use the minibuffer contents with exit-minibuffer, > > not an obsolete completion candidate that remains selected. > > > > -- -- -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Slow completion-at-point was Re: Navigating completions from minibuffer 2023-11-08 17:18 ` T.V Raman @ 2023-11-08 22:11 ` T.V Raman 2023-11-09 7:22 ` Slow completion-at-point Juri Linkov ` (2 more replies) 0 siblings, 3 replies; 62+ messages in thread From: T.V Raman @ 2023-11-08 22:11 UTC (permalink / raw) To: emacs-devel; +Cc: emacs-devel, juri Here is some timing information for this issue: I added the following around advice fragment to completion-at-point debug: (let ((start (current-time))) ad-do-it (message "<%.4f %d gcs %.4f>" (float-time (time-subtract (current-time) start)) gcs-done gc-elapsed)) Then I went to a shell buffer, and from my home directory (it contains a subdir text) typed cd te <tab> Messages buffer shows the following: ~/ Making completion list... Sole completion <2.0219 14 gcs 1.2927> --Raman T.V Raman writes: > Adding emacs-devel after verifying slowness: > > 1. completion-at-point appears to have gotten very slow -- from memory > in the last week (emacs built against Git @HEAD) > The slowness is not present in a build from October 4. > > > > T.V Raman writes: > > Juri Linkov <juri@linkov.net> writes: > > > > > > Agreed. And somewhat related and something that has been causing me > > trouble: > > > > Q: Why is completion-at-point *soo much* slower than > > hippie-expand? > > > > I tried to understand why by looking at the code for completion-at-point > > but failed miserably. > > > > Hope you could look at this since you're working on completion related > > bits. > > > > > > Also, how and when completions are displayed can now be controlled by > > multiple custom knobs, but it's hard to map the combinatorial explosion > > of the available possibilities to different user experiences without > > trying all possible settings; a higher level overview with a couple of > > recipes that describe common combinations would help. > > > > > > >> everything works, now that I actually applied the complete patch:-) > > > > > > Thanks for confirming and for suggesting this change, now pushed. > > > > > > Probably we have to make RET more smart, so that when more editing > > > was performed in the minibuffer after the completions were displayed, > > > then to use the minibuffer contents with exit-minibuffer, > > > not an obsolete completion candidate that remains selected. > > > > > > > -- > > -- > > -- -- -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-08 22:11 ` Slow completion-at-point was " T.V Raman @ 2023-11-09 7:22 ` Juri Linkov 2023-11-09 12:10 ` Dmitry Gutov 2023-11-09 15:39 ` T.V Raman 2023-11-09 12:20 ` Slow completion-at-point was Re: Navigating completions from minibuffer Dmitry Gutov 2023-11-09 17:46 ` T.V Raman 2 siblings, 2 replies; 62+ messages in thread From: Juri Linkov @ 2023-11-09 7:22 UTC (permalink / raw) To: T.V Raman; +Cc: emacs-devel > Here is some timing information for this issue: > > I added the following around advice fragment to completion-at-point debug: > > (let ((start (current-time))) > ad-do-it > (message "<%.4f %d gcs %.4f>" > (float-time (time-subtract (current-time) start)) > gcs-done gc-elapsed)) > > Then I went to a shell buffer, and from my home directory (it contains > a subdir text) typed > > cd te <tab> > > Messages buffer shows the following: > > ~/ > Making completion list... > Sole completion > <2.0219 14 gcs 1.2927> Please try to get benchmark numbers after customizing the recently added 'completion-lazy-hilit'. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-09 7:22 ` Slow completion-at-point Juri Linkov @ 2023-11-09 12:10 ` Dmitry Gutov 2023-11-09 16:20 ` Juri Linkov 2023-11-11 2:48 ` T.V Raman 2023-11-09 15:39 ` T.V Raman 1 sibling, 2 replies; 62+ messages in thread From: Dmitry Gutov @ 2023-11-09 12:10 UTC (permalink / raw) To: Juri Linkov, T.V Raman; +Cc: emacs-devel On 09/11/2023 09:22, Juri Linkov wrote: >> Here is some timing information for this issue: >> >> I added the following around advice fragment to completion-at-point debug: >> >> (let ((start (current-time))) >> ad-do-it >> (message "<%.4f %d gcs %.4f>" >> (float-time (time-subtract (current-time) start)) >> gcs-done gc-elapsed)) >> >> Then I went to a shell buffer, and from my home directory (it contains >> a subdir text) typed >> >> cd te <tab> >> >> Messages buffer shows the following: >> >> ~/ >> Making completion list... >> Sole completion >> <2.0219 14 gcs 1.2927> > Please try to get benchmark numbers after customizing the recently added > 'completion-lazy-hilit'. completion-lazy-hilit is not a user option. It's a defvar, intended for completion frontends to opt into deferred highlighting performed by completions styles. IOW, whenever each completion UI is updated (that uses completion styles -- that is, calls completion-all-completions), all its users will see the benefits. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-09 12:10 ` Dmitry Gutov @ 2023-11-09 16:20 ` Juri Linkov 2023-11-09 18:32 ` João Távora 2023-11-11 2:48 ` T.V Raman 1 sibling, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-09 16:20 UTC (permalink / raw) To: Dmitry Gutov; +Cc: emacs-devel > IOW, whenever each completion UI is updated (that uses completion styles -- > that is, calls completion-all-completions), all its users will see the > benefits. BTW, after the recent change many tests are broken at the first part of test/lisp/minibuffer-tests.el since there is no text property 'completion-score' anymore. I noticed this when adding more tests at the second part of the same file that is unrelated to this change. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-09 16:20 ` Juri Linkov @ 2023-11-09 18:32 ` João Távora 0 siblings, 0 replies; 62+ messages in thread From: João Távora @ 2023-11-09 18:32 UTC (permalink / raw) To: Juri Linkov; +Cc: Dmitry Gutov, emacs-devel [-- Attachment #1: Type: text/plain, Size: 743 bytes --] On Thu, Nov 9, 2023, 16:22 Juri Linkov <juri@linkov.net> wrote: > > IOW, whenever each completion UI is updated (that uses completion styles > -- > > that is, calls completion-all-completions), all its users will see the > > benefits. > > BTW, after the recent change many tests are broken at the first part > of test/lisp/minibuffer-tests.el since there is no text property > 'completion-score' anymore. I noticed this when adding more tests > at the second part of the same file that is unrelated to this change. Thanks for noticing and sorry for the breakage. I think the tests need to be updated to reflect what exactly the public expectations are regarding these properties. I'll look into this ASAP. João > > [-- Attachment #2: Type: text/html, Size: 1285 bytes --] ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-09 12:10 ` Dmitry Gutov 2023-11-09 16:20 ` Juri Linkov @ 2023-11-11 2:48 ` T.V Raman 2023-11-11 10:36 ` Dmitry Gutov 1 sibling, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-11 2:48 UTC (permalink / raw) To: Dmitry Gutov; +Cc: Juri Linkov, emacs-devel Dmitry Gutov <dmitry@gutov.dev> writes: Not sure if you saw my message where I tracked down the culprit: The slowness in completion-at-point is triggered if Completion Auto Help: Choice: Value Menu Always visible. is set as above, it gets janky; if you go back to the default it performs normally. > On 09/11/2023 09:22, Juri Linkov wrote: >>> Here is some timing information for this issue: >>> >>> I added the following around advice fragment to completion-at-point debug: >>> >>> (let ((start (current-time))) >>> ad-do-it >>> (message "<%.4f %d gcs %.4f>" >>> (float-time (time-subtract (current-time) start)) >>> gcs-done gc-elapsed)) >>> >>> Then I went to a shell buffer, and from my home directory (it contains >>> a subdir text) typed >>> >>> cd te <tab> >>> >>> Messages buffer shows the following: >>> >>> ~/ >>> Making completion list... >>> Sole completion >>> <2.0219 14 gcs 1.2927> >> Please try to get benchmark numbers after customizing the recently added >> 'completion-lazy-hilit'. > > completion-lazy-hilit is not a user option. It's a defvar, intended > for completion frontends to opt into deferred highlighting performed > by completions styles. > > IOW, whenever each completion UI is updated (that uses completion > styles -- that is, calls completion-all-completions), all its users > will see the benefits. > -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-11 2:48 ` T.V Raman @ 2023-11-11 10:36 ` Dmitry Gutov 2023-11-11 16:40 ` T.V Raman 0 siblings, 1 reply; 62+ messages in thread From: Dmitry Gutov @ 2023-11-11 10:36 UTC (permalink / raw) To: T.V Raman; +Cc: Juri Linkov, emacs-devel On 11/11/2023 04:48, T.V Raman wrote: > Dmitry Gutov<dmitry@gutov.dev> writes: > > > Not sure if you saw my message where I tracked down the culprit: > The slowness in completion-at-point is triggered if > Completion Auto Help: Choice: Value Menu Always visible. > > is set as above, it gets janky; if you go back to the default it > performs normally. This is not a new option: it's from 2008. It is somewhat natural for things to get slower if Emacs has to do more work - i.e. the completions buffer remains visible. Did the effect of this option get worse over the last week, or did you just try setting it to 'always' very recently? In any case, if you have a good repro for the problem, you can file a bug report now. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-11 10:36 ` Dmitry Gutov @ 2023-11-11 16:40 ` T.V Raman 2023-11-11 19:00 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-11 16:40 UTC (permalink / raw) To: Dmitry Gutov; +Cc: Juri Linkov, emacs-devel Dmitry Gutov <dmitry@gutov.dev> writes: I've always had it set to always, and the slowness only emerged this week -- ie when I sent the message. > On 11/11/2023 04:48, T.V Raman wrote: >> Dmitry Gutov<dmitry@gutov.dev> writes: >> Not sure if you saw my message where I tracked down the culprit: >> The slowness in completion-at-point is triggered if >> Completion Auto Help: Choice: Value Menu Always visible. >> is set as above, it gets janky; if you go back to the default it >> performs normally. > > This is not a new option: it's from 2008. It is somewhat natural for > things to get slower if Emacs has to do more work - i.e. the > completions buffer remains visible. > > Did the effect of this option get worse over the last week, or did you > just try setting it to 'always' very recently? > > In any case, if you have a good repro for the problem, you can file a > bug report now. > -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-11 16:40 ` T.V Raman @ 2023-11-11 19:00 ` Juri Linkov 2023-11-11 19:43 ` T.V Raman 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-11 19:00 UTC (permalink / raw) To: T.V Raman; +Cc: Dmitry Gutov, emacs-devel > I've always had it set to always, and the slowness only emerged this > week -- ie when I sent the message. I guess the slowness might be caused by one of these two commit: either the commit f0c0ff6bf23 or the commit dfffb91a705. Could you please try to check out whether the state before these commits caused the same slowness? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-11 19:00 ` Juri Linkov @ 2023-11-11 19:43 ` T.V Raman 2023-11-11 21:50 ` Dmitry Gutov 0 siblings, 1 reply; 62+ messages in thread From: T.V Raman @ 2023-11-11 19:43 UTC (permalink / raw) To: Juri Linkov; +Cc: Dmitry Gutov, emacs-devel could you give me the exact SHA checksum to checkout? -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-11 19:43 ` T.V Raman @ 2023-11-11 21:50 ` Dmitry Gutov 0 siblings, 0 replies; 62+ messages in thread From: Dmitry Gutov @ 2023-11-11 21:50 UTC (permalink / raw) To: T.V Raman, Juri Linkov; +Cc: emacs-devel On 11/11/2023 21:43, T.V Raman wrote: > could you give me the exact SHA checksum to checkout? These shorter hashes are fine to use with 'git checkout'. But you might need to 'git fetch' first. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point 2023-11-09 7:22 ` Slow completion-at-point Juri Linkov 2023-11-09 12:10 ` Dmitry Gutov @ 2023-11-09 15:39 ` T.V Raman 1 sibling, 0 replies; 62+ messages in thread From: T.V Raman @ 2023-11-09 15:39 UTC (permalink / raw) To: juri; +Cc: raman, emacs-devel So I played around with some of the completion related custom settings and at some point the problem disappeared. Will try what you suggest if it comes back. Juri Linkov writes: > > Here is some timing information for this issue: > > > > I added the following around advice fragment to completion-at-point debug: > > > > (let ((start (current-time))) > > ad-do-it > > (message "<%.4f %d gcs %.4f>" > > (float-time (time-subtract (current-time) start)) > > gcs-done gc-elapsed)) > > > > Then I went to a shell buffer, and from my home directory (it contains > > a subdir text) typed > > > > cd te <tab> > > > > Messages buffer shows the following: > > > > ~/ > > Making completion list... > > Sole completion > > <2.0219 14 gcs 1.2927> > > Please try to get benchmark numbers after customizing the recently added > 'completion-lazy-hilit'. -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point was Re: Navigating completions from minibuffer 2023-11-08 22:11 ` Slow completion-at-point was " T.V Raman 2023-11-09 7:22 ` Slow completion-at-point Juri Linkov @ 2023-11-09 12:20 ` Dmitry Gutov 2023-11-09 15:41 ` T.V Raman 2023-11-09 17:46 ` T.V Raman 2 siblings, 1 reply; 62+ messages in thread From: Dmitry Gutov @ 2023-11-09 12:20 UTC (permalink / raw) To: T.V Raman, emacs-devel; +Cc: juri On 09/11/2023 00:11, T.V Raman wrote: > Here is some timing information for this issue: > > > > I added the following around advice fragment to completion-at-point debug: > > (let ((start (current-time))) > ad-do-it > (message "<%.4f %d gcs %.4f>" > (float-time (time-subtract (current-time) start)) > gcs-done gc-elapsed)) > > Then I went to a shell buffer, and from my home directory (it contains > a subdir text) typed > > cd te <tab> > > Messages buffer shows the following: > > ~/ > Making completion list... > Sole completion > <2.0219 14 gcs 1.2927> Is completion-at-point slow for you in shell buffers in particular, or everywhere else too? E.g. completion in 'C-h f'. There was a change last week (dfffb91a70532ac002), but it was supposed to make completion faster. And in your tests, it did (at least for icomplete, which knows how to take advantage of the optimization). ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Slow completion-at-point was Re: Navigating completions from minibuffer 2023-11-09 12:20 ` Slow completion-at-point was Re: Navigating completions from minibuffer Dmitry Gutov @ 2023-11-09 15:41 ` T.V Raman 0 siblings, 0 replies; 62+ messages in thread From: T.V Raman @ 2023-11-09 15:41 UTC (permalink / raw) To: dmitry; +Cc: raman, emacs-devel, juri when the problem was present, it was slow everywhere, though shell is where I noticed it first. See other reply -- the problem disappeared last night as I was twiddling various completion related custom knobs; sadly I dont know when exactly it disappeared. Dmitry Gutov writes: > On 09/11/2023 00:11, T.V Raman wrote: > > Here is some timing information for this issue: > > > > > > > > I added the following around advice fragment to completion-at-point debug: > > > > (let ((start (current-time))) > > ad-do-it > > (message "<%.4f %d gcs %.4f>" > > (float-time (time-subtract (current-time) start)) > > gcs-done gc-elapsed)) > > > > Then I went to a shell buffer, and from my home directory (it contains > > a subdir text) typed > > > > cd te <tab> > > > > Messages buffer shows the following: > > > > ~/ > > Making completion list... > > Sole completion > > <2.0219 14 gcs 1.2927> > > Is completion-at-point slow for you in shell buffers in particular, or > everywhere else too? E.g. completion in 'C-h f'. > > There was a change last week (dfffb91a70532ac002), but it was supposed > to make completion faster. And in your tests, it did (at least for > icomplete, which knows how to take advantage of the optimization). -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Slow completion-at-point was Re: Navigating completions from minibuffer 2023-11-08 22:11 ` Slow completion-at-point was " T.V Raman 2023-11-09 7:22 ` Slow completion-at-point Juri Linkov 2023-11-09 12:20 ` Slow completion-at-point was Re: Navigating completions from minibuffer Dmitry Gutov @ 2023-11-09 17:46 ` T.V Raman 2 siblings, 0 replies; 62+ messages in thread From: T.V Raman @ 2023-11-09 17:46 UTC (permalink / raw) To: emacs-devel; +Cc: emacs-devel, juri Found the culprit: If custom option Hide Completion Auto Help: Choice: Value Menu Always visible is set as above, it gets janky; if you go back to the default it performs normally. Haven't tested all other possibilities. T.V Raman writes: > > Here is some timing information for this issue: > > > > I added the following around advice fragment to completion-at-point debug: > > (let ((start (current-time))) > ad-do-it > (message "<%.4f %d gcs %.4f>" > (float-time (time-subtract (current-time) start)) > gcs-done gc-elapsed)) > > Then I went to a shell buffer, and from my home directory (it contains > a subdir text) typed > > cd te <tab> > > Messages buffer shows the following: > > ~/ > Making completion list... > Sole completion > <2.0219 14 gcs 1.2927> > --Raman > T.V Raman writes: > > Adding emacs-devel after verifying slowness: > > > > 1. completion-at-point appears to have gotten very slow -- from memory > > in the last week (emacs built against Git @HEAD) > > The slowness is not present in a build from October 4. > > > > > > > > T.V Raman writes: > > > Juri Linkov <juri@linkov.net> writes: > > > > > > > > > Agreed. And somewhat related and something that has been causing me > > > trouble: > > > > > > Q: Why is completion-at-point *soo much* slower than > > > hippie-expand? > > > > > > I tried to understand why by looking at the code for completion-at-point > > > but failed miserably. > > > > > > Hope you could look at this since you're working on completion related > > > bits. > > > > > > > > > Also, how and when completions are displayed can now be controlled by > > > multiple custom knobs, but it's hard to map the combinatorial explosion > > > of the available possibilities to different user experiences without > > > trying all possible settings; a higher level overview with a couple of > > > recipes that describe common combinations would help. > > > > > > > > > >> everything works, now that I actually applied the complete patch:-) > > > > > > > > Thanks for confirming and for suggesting this change, now pushed. > > > > > > > > Probably we have to make RET more smart, so that when more editing > > > > was performed in the minibuffer after the completions were displayed, > > > > then to use the minibuffer contents with exit-minibuffer, > > > > not an obsolete completion candidate that remains selected. > > > > > > > > > > -- > > > > -- > > > > -- > > -- > > -- -- -- -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-08 7:39 ` Juri Linkov 2023-11-08 16:21 ` T.V Raman @ 2023-11-10 13:12 ` Spencer Baugh 2023-11-11 18:58 ` Juri Linkov 1 sibling, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-10 13:12 UTC (permalink / raw) To: emacs-devel Juri Linkov <juri@linkov.net> writes: >> everything works, now that I actually applied the complete patch:-) > > Thanks for confirming and for suggesting this change, now pushed. > > Probably we have to make RET more smart, so that when more editing > was performed in the minibuffer after the completions were displayed, > then to use the minibuffer contents with exit-minibuffer, > not an obsolete completion candidate that remains selected. Interesting solution! So if the last relevant command was changing the selected candidate, then RET submits the candidate; if the last relevant command was editing the minibuffer, then RET submits the minibuffer text. What about the case where I type some text, switch between some completion candidates in *Completions*, then decide I don't want any of them and hit RET to submit the minibuffer text? That's something that works today. Can we support that somehow? I wrote a series of experimental changes where RET picks the selected completion candidate, after discussing that sort of behavior in the completions-auto-update thread, and used them for a while. I ended up concluding that it couldn't be done without running into bugs/loss of funtionality like icomplete and ido have, and that therefore a separate M-RET was the best way. But what you propose here would solve some of the issues, and maybe all of them. BTW, I think the best approach I came up with in those experiments was for TAB to insert the selected completion candidate. Then TAB RET effectively chooses the selected candidate. I found that quite elegant and powerful, but in the end it also was limiting in some cases. But maybe in combination with your idea of only inserting the selected completion candidate if the minibuffer hasn't changed since selecting it, it could work. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-10 13:12 ` Spencer Baugh @ 2023-11-11 18:58 ` Juri Linkov 2023-11-14 7:36 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-11 18:58 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel >> Probably we have to make RET more smart, so that when more editing >> was performed in the minibuffer after the completions were displayed, >> then to use the minibuffer contents with exit-minibuffer, >> not an obsolete completion candidate that remains selected. > > Interesting solution! So if the last relevant command was changing the > selected candidate, then RET submits the candidate; if the last relevant > command was editing the minibuffer, then RET submits the minibuffer > text. > > What about the case where I type some text, switch between some > completion candidates in *Completions*, then decide I don't want any of > them and hit RET to submit the minibuffer text? That's something that > works today. Can we support that somehow? Currently the suggested way to avoid selecting a highlighted candidate is to close the completions window with C-g, i.e. it adheres to the principle “what you see is what you get” - when you see a highlighted candidate then you will get it, otherwise you will get the minibuffer contents. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-11 18:58 ` Juri Linkov @ 2023-11-14 7:36 ` Juri Linkov 2023-11-15 21:40 ` Spencer Baugh 2023-11-15 22:03 ` Spencer Baugh 0 siblings, 2 replies; 62+ messages in thread From: Juri Linkov @ 2023-11-14 7:36 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel >>> Probably we have to make RET more smart, so that when more editing >>> was performed in the minibuffer after the completions were displayed, >>> then to use the minibuffer contents with exit-minibuffer, >>> not an obsolete completion candidate that remains selected. >> >> Interesting solution! So if the last relevant command was changing the >> selected candidate, then RET submits the candidate; if the last relevant >> command was editing the minibuffer, then RET submits the minibuffer >> text. >> >> What about the case where I type some text, switch between some >> completion candidates in *Completions*, then decide I don't want any of >> them and hit RET to submit the minibuffer text? That's something that >> works today. Can we support that somehow? > > Currently the suggested way to avoid selecting a highlighted candidate > is to close the completions window with C-g, i.e. it adheres > to the principle “what you see is what you get” - when you see > a highlighted candidate then you will get it, otherwise you will get > the minibuffer contents. So there are at least 2 variants what to do when completing-read is used as selection rather than completion: 1. On editing the minibuffer close the completions window as expired. Shouldn't 'completion-auto-help' support this case? Should it have a new value 'close'? Or need to create a new option 'completion-auto-close'? 2. AFAIR, 'completion-auto-update' already closes the completions window if none of completion candidates matches the contents of the minibuffer? So editing the minibuffer never causes a situation where a highlighted candidate doesn't match the minibuffer contents? And typing some characters to narrow down the completions always keeps the highlighted candidate selected? Then there is no problem. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-14 7:36 ` Juri Linkov @ 2023-11-15 21:40 ` Spencer Baugh 2023-11-16 17:15 ` T.V Raman 2023-11-15 22:03 ` Spencer Baugh 1 sibling, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-15 21:40 UTC (permalink / raw) To: emacs-devel Juri Linkov <juri@linkov.net> writes: >>>> Probably we have to make RET more smart, so that when more editing >>>> was performed in the minibuffer after the completions were displayed, >>>> then to use the minibuffer contents with exit-minibuffer, >>>> not an obsolete completion candidate that remains selected. >>> >>> Interesting solution! So if the last relevant command was changing the >>> selected candidate, then RET submits the candidate; if the last relevant >>> command was editing the minibuffer, then RET submits the minibuffer >>> text. >>> >>> What about the case where I type some text, switch between some >>> completion candidates in *Completions*, then decide I don't want any of >>> them and hit RET to submit the minibuffer text? That's something that >>> works today. Can we support that somehow? >> >> Currently the suggested way to avoid selecting a highlighted candidate >> is to close the completions window with C-g, i.e. it adheres >> to the principle “what you see is what you get” - when you see >> a highlighted candidate then you will get it, otherwise you will get >> the minibuffer contents. > > So there are at least 2 variants what to do when completing-read is used > as selection rather than completion: > > 1. On editing the minibuffer close the completions window as expired. > > Shouldn't 'completion-auto-help' support this case? > Should it have a new value 'close'? > Or need to create a new option 'completion-auto-close'? I think a new option would be better, the different options for completion-auto-help all seem plausible to combine with that behavior. It's a bit tricky since completion-auto-update=t effectively implies completion-auto-close=t. Oh, actually, perhaps completion-auto-close should be merged with completion-auto-update. Then completion-auto-update has three values: - nil (today's behavior) - close (close the completions window on editing the minibufer) - t (auto-update the completions window on editing the minibuffer) After all, both non-nil values would use basically the same implementation. > 2. AFAIR, 'completion-auto-update' already closes the completions window > if none of completion candidates matches the contents of the minibuffer? > > So editing the minibuffer never causes a situation where a highlighted > candidate doesn't match the minibuffer contents? > > And typing some characters to narrow down the completions > always keeps the highlighted candidate selected? > > Then there is no problem. Yes, that's correct. Very interesting analysis! This makes sense. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-15 21:40 ` Spencer Baugh @ 2023-11-16 17:15 ` T.V Raman 0 siblings, 0 replies; 62+ messages in thread From: T.V Raman @ 2023-11-16 17:15 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel Spencer Baugh <sbaugh@janestreet.com> writes: This is a good idea. I still believe we have too many completion knobs, but I dont know how else to simplify things other than one step at a time and trying it out incrementally. But we should do this with the final goal of recuding the current combinatorial exposion of choices which makes things both hard to test and to explain. > Juri Linkov <juri@linkov.net> writes: >>>>> Probably we have to make RET more smart, so that when more editing >>>>> was performed in the minibuffer after the completions were displayed, >>>>> then to use the minibuffer contents with exit-minibuffer, >>>>> not an obsolete completion candidate that remains selected. >>>> >>>> Interesting solution! So if the last relevant command was changing the >>>> selected candidate, then RET submits the candidate; if the last relevant >>>> command was editing the minibuffer, then RET submits the minibuffer >>>> text. >>>> >>>> What about the case where I type some text, switch between some >>>> completion candidates in *Completions*, then decide I don't want any of >>>> them and hit RET to submit the minibuffer text? That's something that >>>> works today. Can we support that somehow? >>> >>> Currently the suggested way to avoid selecting a highlighted candidate >>> is to close the completions window with C-g, i.e. it adheres >>> to the principle “what you see is what you get” - when you see >>> a highlighted candidate then you will get it, otherwise you will get >>> the minibuffer contents. >> >> So there are at least 2 variants what to do when completing-read is used >> as selection rather than completion: >> >> 1. On editing the minibuffer close the completions window as expired. >> >> Shouldn't 'completion-auto-help' support this case? >> Should it have a new value 'close'? >> Or need to create a new option 'completion-auto-close'? > > I think a new option would be better, the different options for > completion-auto-help all seem plausible to combine with that behavior. > > It's a bit tricky since completion-auto-update=t effectively implies > completion-auto-close=t. > > Oh, actually, perhaps completion-auto-close should be merged with > completion-auto-update. Then completion-auto-update has three values: > > - nil (today's behavior) > - close (close the completions window on editing the minibufer) > - t (auto-update the completions window on editing the minibuffer) > > After all, both non-nil values would use basically the same > implementation. > >> 2. AFAIR, 'completion-auto-update' already closes the completions window >> if none of completion candidates matches the contents of the minibuffer? >> >> So editing the minibuffer never causes a situation where a highlighted >> candidate doesn't match the minibuffer contents? >> >> And typing some characters to narrow down the completions >> always keeps the highlighted candidate selected? >> >> Then there is no problem. > > Yes, that's correct. > > Very interesting analysis! This makes sense. > > -- ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-14 7:36 ` Juri Linkov 2023-11-15 21:40 ` Spencer Baugh @ 2023-11-15 22:03 ` Spencer Baugh 2023-11-16 7:16 ` Juri Linkov 1 sibling, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-15 22:03 UTC (permalink / raw) To: emacs-devel Juri Linkov <juri@linkov.net> writes: >>>> Probably we have to make RET more smart, so that when more editing >>>> was performed in the minibuffer after the completions were displayed, >>>> then to use the minibuffer contents with exit-minibuffer, >>>> not an obsolete completion candidate that remains selected. >>> >>> Interesting solution! So if the last relevant command was changing the >>> selected candidate, then RET submits the candidate; if the last relevant >>> command was editing the minibuffer, then RET submits the minibuffer >>> text. >>> >>> What about the case where I type some text, switch between some >>> completion candidates in *Completions*, then decide I don't want any of >>> them and hit RET to submit the minibuffer text? That's something that >>> works today. Can we support that somehow? >> >> Currently the suggested way to avoid selecting a highlighted candidate >> is to close the completions window with C-g, i.e. it adheres >> to the principle “what you see is what you get” - when you see >> a highlighted candidate then you will get it, otherwise you will get >> the minibuffer contents. > > So there are at least 2 variants what to do when completing-read is used > as selection rather than completion: > > 1. On editing the minibuffer close the completions window as expired. This would make it difficult to narrow the completions by typing in some text from them, though. I think I relatively often: 1. TAB 2. Look at *Completions* 3. Type in some text from one of them Step 3 would harder if *Completions* disappeared on the first character I typed. Perhaps instead typing a character could deselect the currently selected completion, rather than hide *Completions*? But it seems nice to maintain the selected completion as you type, as a visual guide, and also my other patch maintains the selected completion between each TAB, which is nice. Maybe... we could somehow de-activate the selected completion, visually de-emphasizing it in some way, but still showing its position in some less-significant way? An underline, perhaps? And subsequent operations which change the selected completion would reactivate it, making it the selected completion again. That might be a bit tricky to represent visually in an intuitive way, but it might give us everything we want. Maybe we could represent that visually by moving the selected completion indicator (which in emacs -q is a green highlight) to *the minibuffer* when the "selected" completion is not actually active. Then the user would quite quickly get the idea: RET submits whatever is highlighted in green. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-15 22:03 ` Spencer Baugh @ 2023-11-16 7:16 ` Juri Linkov 2023-11-16 14:41 ` Spencer Baugh 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-16 7:16 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel >> 1. On editing the minibuffer close the completions window as expired. > > This would make it difficult to narrow the completions by typing in some > text from them, though. I think I relatively often: > > 1. TAB > 2. Look at *Completions* > 3. Type in some text from one of them > > Step 3 would harder if *Completions* disappeared on the first character > I typed. > > Perhaps instead typing a character could deselect the currently selected > completion, rather than hide *Completions*? But it seems nice to > maintain the selected completion as you type, as a visual guide, and > also my other patch maintains the selected completion between each TAB, > which is nice. In case of completion-auto-update=t the selected completion should be maintained as you type indeed. But when completion-auto-update=nil instead of forcing the completions window to be closed it looks better just to deselect the current completion. > Maybe... we could somehow de-activate the selected completion, visually > de-emphasizing it in some way, but still showing its position in some > less-significant way? An underline, perhaps? And subsequent operations > which change the selected completion would reactivate it, making it the > selected completion again. That might be a bit tricky to represent > visually in an intuitive way, but it might give us everything we want. > > Maybe we could represent that visually by moving the selected completion > indicator (which in emacs -q is a green highlight) to *the minibuffer* > when the "selected" completion is not actually active. Then the user > would quite quickly get the idea: RET submits whatever is highlighted in > green. Usually the active editing area is not highlighted in any way. So better would be to use highlighting only in the completions window. For completions-highlight-face=t it's clear that deselection should remove this face. But what to do for completions-highlight-face=nil is not clear. Maybe just to move point to area with no candidates? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-16 7:16 ` Juri Linkov @ 2023-11-16 14:41 ` Spencer Baugh 2023-11-16 17:28 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-16 14:41 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>> 1. On editing the minibuffer close the completions window as expired. >> >> This would make it difficult to narrow the completions by typing in some >> text from them, though. I think I relatively often: >> >> 1. TAB >> 2. Look at *Completions* >> 3. Type in some text from one of them >> >> Step 3 would harder if *Completions* disappeared on the first character >> I typed. >> >> Perhaps instead typing a character could deselect the currently selected >> completion, rather than hide *Completions*? But it seems nice to >> maintain the selected completion as you type, as a visual guide, and >> also my other patch maintains the selected completion between each TAB, >> which is nice. > > In case of completion-auto-update=t the selected completion should be > maintained as you type indeed. But when completion-auto-update=nil > instead of forcing the completions window to be closed it looks better > just to deselect the current completion. Yes, agreed on that point. When completion-auto-update=nil, It is better to deselect the current completion than close the completions window. >> Maybe... we could somehow de-activate the selected completion, visually >> de-emphasizing it in some way, but still showing its position in some >> less-significant way? An underline, perhaps? And subsequent operations >> which change the selected completion would reactivate it, making it the >> selected completion again. That might be a bit tricky to represent >> visually in an intuitive way, but it might give us everything we want. >> >> Maybe we could represent that visually by moving the selected completion >> indicator (which in emacs -q is a green highlight) to *the minibuffer* >> when the "selected" completion is not actually active. Then the user >> would quite quickly get the idea: RET submits whatever is highlighted in >> green. > > Usually the active editing area is not highlighted in any way. > So better would be to use highlighting only in the completions window. Yes, but I wonder if we could have some simple indicator in the minibuffer which isn't too visually noisy. And which is only activated if the user has previously selected a candidate in *Completions* and then deselected it by typing. Maybe highlighting the minibuffer prompt instead of the text? Well, not really necessary, just a thought. > For completions-highlight-face=t it's clear that deselection should > remove this face. But what to do for completions-highlight-face=nil > is not clear. Maybe just to move point to area with no candidates? I had been assuming "move point to area with no candidates" was how we would implement deselection in any case. Since "point is on a candidate" is the definition of selection, as it stands. Maybe we can deselect by moving point to just before (or after) the selected candidate? Move point to the whitespace in-between candidates. Then no candidate is selected, and completions-highlight-face won't highlight any candidate, but there's still a bit of visual indicator (window-point) which shows what candidate was previously selected, and if the user does want to re-select the candidate, the user can just hit <down> (or <up>) to select the candidate again. And completion-auto-update could maintain this state, too, just like it maintains the selected candidate: if point is right before (but not on) a candidate, it should stay right before (but not on) that candidate as the user types and *Completions* updates. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-16 14:41 ` Spencer Baugh @ 2023-11-16 17:28 ` Juri Linkov 2023-11-16 18:25 ` Spencer Baugh 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-16 17:28 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel > Maybe we can deselect by moving point to just before (or after) the > selected candidate? Move point to the whitespace in-between candidates. > Then no candidate is selected, and completions-highlight-face won't > highlight any candidate, but there's still a bit of visual indicator > (window-point) which shows what candidate was previously selected, and > if the user does want to re-select the candidate, the user can just hit > <down> (or <up>) to select the candidate again. In fact, the whitespace in-between candidates is the only place where point could be moved to deselect the candidate. Because there is no space at the end of the completions buffer, and even at the beginning when both completion-show-help and completions-header-format are nil. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-16 17:28 ` Juri Linkov @ 2023-11-16 18:25 ` Spencer Baugh 2023-11-17 7:09 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-16 18:25 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >> Maybe we can deselect by moving point to just before (or after) the >> selected candidate? Move point to the whitespace in-between candidates. >> Then no candidate is selected, and completions-highlight-face won't >> highlight any candidate, but there's still a bit of visual indicator >> (window-point) which shows what candidate was previously selected, and >> if the user does want to re-select the candidate, the user can just hit >> <down> (or <up>) to select the candidate again. > > In fact, the whitespace in-between candidates is the only place where > point could be moved to deselect the candidate. Because there is no space > at the end of the completions buffer, and even at the beginning when > both completion-show-help and completions-header-format are nil. Oh, great! So then let's just do that. Possibly we should ensure there is one character worth of whitespace at the end of the completions buffer, so we can consistently just move point to right after the selected candidate to deselect it. Also... doesn't this mean that with (setq completion-show-help nil completions-header-format nil) *Completions* will start out with the point on the first completion candidate? So M-RET will select it, and with minibuffer-visible-completions=t, even RET will select it? Maybe point should uniformly start at the end of *Completions* instead of at the start? In the one character worth of whitespace we might add? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-16 18:25 ` Spencer Baugh @ 2023-11-17 7:09 ` Juri Linkov 2023-11-17 17:22 ` Spencer Baugh 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-17 7:09 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel > Also... doesn't this mean that with > > (setq completion-show-help nil > completions-header-format nil) > > *Completions* will start out with the point on the first completion > candidate? So M-RET will select it, and with > minibuffer-visible-completions=t, even RET will select it? > > Maybe point should uniformly start at the end of *Completions* instead > of at the start? In the one character worth of whitespace we might add? Starting at the end of *Completions* would work only when completion-auto-wrap is t. Another variant is to add a narrow character at the beginning of *Completions* like used for an empty rectangular region in rectangle-mark-mode. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-17 7:09 ` Juri Linkov @ 2023-11-17 17:22 ` Spencer Baugh 2023-11-18 20:58 ` sbaugh 0 siblings, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-17 17:22 UTC (permalink / raw) To: emacs-devel Juri Linkov <juri@linkov.net> writes: >> Also... doesn't this mean that with >> >> (setq completion-show-help nil >> completions-header-format nil) >> >> *Completions* will start out with the point on the first completion >> candidate? So M-RET will select it, and with >> minibuffer-visible-completions=t, even RET will select it? >> >> Maybe point should uniformly start at the end of *Completions* instead >> of at the start? In the one character worth of whitespace we might add? > > Starting at the end of *Completions* would work only when > completion-auto-wrap is t. Another variant is to add a narrow > character at the beginning of *Completions* like used for > an empty rectangular region in rectangle-mark-mode. Yes, that sounds good. And then we could uniformly move point before the candidate when deselecting it, which IMO would be a little visually nicer. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-17 17:22 ` Spencer Baugh @ 2023-11-18 20:58 ` sbaugh 2023-11-19 7:08 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: sbaugh @ 2023-11-18 20:58 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1336 bytes --] Spencer Baugh <sbaugh@janestreet.com> writes: > Juri Linkov <juri@linkov.net> writes: >>> Also... doesn't this mean that with >>> >>> (setq completion-show-help nil >>> completions-header-format nil) >>> >>> *Completions* will start out with the point on the first completion >>> candidate? So M-RET will select it, and with >>> minibuffer-visible-completions=t, even RET will select it? >>> >>> Maybe point should uniformly start at the end of *Completions* instead >>> of at the start? In the one character worth of whitespace we might add? >> >> Starting at the end of *Completions* would work only when >> completion-auto-wrap is t. Another variant is to add a narrow >> character at the beginning of *Completions* like used for >> an empty rectangular region in rectangle-mark-mode. > > Yes, that sounds good. And then we could uniformly move point before > the candidate when deselecting it, which IMO would be a little visually > nicer. OK, how about this? It's an adapted version of my completions-auto-update patch, which provides (for now) only the feature of deselecting the completion when point moves or the minibuffer changes. The infrastructure for doing this turns out to be basically identical, which is interesting. It sets completions-auto-update to 'deselect by default, which I think is reasonable? [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Deselect-the-selected-completion-candidate-when-typi.patch --] [-- Type: text/x-patch, Size: 5933 bytes --] From beb15769fdacc388a059e6bc79e924bc219bcb98 Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh@catern.com> Date: Sat, 18 Nov 2023 20:55:18 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-visible-completions makes RET submit the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate. * lisp/minibuffer.el (completion--insert): Add a space before each candidate. (completions-auto-update, completions--deselect) (completions--update-if-displayed, completions--after-change) (minibuffer--old-point, completions--post-command): Add. (minibuffer-completion-help): Add completions--after-change and completions--post-command as hooks. (minibuffer-next-completion): Bind completions-auto-update to nil to avoid immediately deselecting the completion. --- lisp/minibuffer.el | 72 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index e0017d88780..026613c9eb2 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2196,15 +2196,18 @@ completion--insert (equal (or (car-safe str) str) selected)) (setq completion--selected-posn (point))) (if (not (consp str)) - (add-text-properties - (point) - (progn - (insert - (if group-fun - (funcall group-fun str 'transform) - str)) - (point)) - `(mouse-face highlight cursor-face ,completions-highlight-face completion--string ,str)) + (progn + ;; We move point to this character to deselect the completion candidate. + (insert " ") + (add-text-properties + (point) + (progn + (insert + (if group-fun + (funcall group-fun str 'transform) + str)) + (point)) + `(mouse-face highlight cursor-face ,completions-highlight-face completion--string ,str))) ;; If `str' is a list that has 2 elements, ;; then the second element is a suffix annotation. ;; If `str' has 3 elements, then the second element @@ -2391,6 +2394,49 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completions-auto-update 'deselect + "If non-nil, change the *Completions* buffer as you type. + +If `deselect', if a completion candidate in *Completions* is +selected (point is on it), it will be deselected (point will be +moved just before it) when the minibuffer point or contents +change. + +This only affects the *Completions* buffer if it is already +displayed." + :type '(choice (const :tag "*Completions* doesn't change as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" deselect)) + :version "30.1") + +(defun completions--deselect () + "If in a completion candidate, move just before the start of it." + (when (get-text-property (point) 'mouse-face) + (when (and (not (bobp)) (get-text-property (1- (point)) 'mouse-face)) + (goto-char (previous-single-property-change (point) 'mouse-face))) + (unless (bobp) + (backward-char 1)))) + +(defun completions--update-if-displayed () + "Update a displayed *Completions* buffer based on `completions-auto-update'" + (when completions-auto-update + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (when (eq completions-auto-update 'deselect) + (completions--deselect)))))) + +(defun completions--after-change () + "Update displayed *Completions* buffer after change in minibuffer contents." + (when (minibufferp) + (completions--update-if-displayed))) + +(defvar-local minibuffer--old-point nil) + +(defun completions--post-command () + "Update displayed *Completions* buffer after change in minibuffer point." + (when (and (minibufferp) (not (eq minibuffer--old-point (point)))) + (setq minibuffer--old-point (point)) + (completions--update-if-displayed))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2413,6 +2459,8 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'post-command-hook #'completions--post-command t) + (remove-hook 'after-change-hook #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2476,6 +2524,9 @@ minibuffer-completion-help (body-function . ,#'(lambda (window) (with-current-buffer mainbuf + (when completions-auto-update + (add-hook 'post-command-hook #'completions--post-command nil t) + (add-hook 'after-change-hook #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4688,7 +4739,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completions-auto-update nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) -- 2.42.1 ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-18 20:58 ` sbaugh @ 2023-11-19 7:08 ` Juri Linkov 2023-11-19 8:19 ` Eli Zaretskii 2023-11-19 14:41 ` Spencer Baugh 0 siblings, 2 replies; 62+ messages in thread From: Juri Linkov @ 2023-11-19 7:08 UTC (permalink / raw) To: sbaugh; +Cc: emacs-devel > OK, how about this? It's an adapted version of my > completions-auto-update patch, which provides (for now) only the feature > of deselecting the completion when point moves or the minibuffer > changes. The infrastructure for doing this turns out to be basically > identical, which is interesting. Indeed, it's easier to start with a simpler case, and then gradually to add handling of more cases to the same option. > It sets completions-auto-update to 'deselect by default, which I think > is reasonable? Isn't deselection needed only when minibuffer-visible-completions is enabled? > minibuffer-visible-completions makes RET submit the selected > completion candidate, if any, ignoring the contents of the minibuffer. > But a user might select a completion candidate and then want to type > something else in the minibuffer and submit what they typed. > > * lisp/minibuffer.el (completion--insert): Add a space before each > candidate. I don't think anyone would like such a space shifting the whole layout to the right. Rather I'd recommend to use a space after each candidate. There is already a space between candidates. Only at the end a space is missing. Or without adding a space at the end we could change `choose-completion` to not select the candidate when point is at the end (`choose-completion` needs fixing anyway since currently it raises an error at the end of the first completion in case of no header.) This still won't solve the case of no header. So in this case for the initial position we could add a narrow line at the top: (propertize "\n" 'face '(:height 0)) This solves a lot of problems, and will help to remove the complicated special-handling of the 'first-completion' text property in many places. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-19 7:08 ` Juri Linkov @ 2023-11-19 8:19 ` Eli Zaretskii 2023-11-19 14:41 ` Spencer Baugh 1 sibling, 0 replies; 62+ messages in thread From: Eli Zaretskii @ 2023-11-19 8:19 UTC (permalink / raw) To: Juri Linkov; +Cc: sbaugh, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: emacs-devel@gnu.org > Date: Sun, 19 Nov 2023 09:08:36 +0200 > > This still won't solve the case of no header. So in this case > for the initial position we could add a narrow line at the top: > > (propertize "\n" 'face '(:height 0)) This only works on GUI frames, no? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-19 7:08 ` Juri Linkov 2023-11-19 8:19 ` Eli Zaretskii @ 2023-11-19 14:41 ` Spencer Baugh 2023-11-19 18:01 ` Juri Linkov 2023-11-23 13:39 ` sbaugh 1 sibling, 2 replies; 62+ messages in thread From: Spencer Baugh @ 2023-11-19 14:41 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >> It sets completions-auto-update to 'deselect by default, which I think >> is reasonable? > > Isn't deselection needed only when minibuffer-visible-completions is enabled? I think we could provide some nice consistency by making it always active. As part of this change, I think we should make sure M-RET will submit a completion candidate even if it's been "deselected". That would be nice because then M-RET serves a useful purpose with minibuffer-visible-completions=t: you can submit the previously-selected completion candidate even if you've typed (causing deselection) since selecting it. With that M-RET behavior, completions-auto-update='deselect doesn't change behavior from Emacs 29, so I think it's a plausible default. And if we do have completions-auto-update='deselect by default, then perhaps we can consider another change to the defaults: make RET always submit the selected completion candidate. That would actually change behavior, since M-<down> followed *immediately* by RET would submit the selected completion candidate, but maybe it's worth it? >> minibuffer-visible-completions makes RET submit the selected >> completion candidate, if any, ignoring the contents of the minibuffer. >> But a user might select a completion candidate and then want to type >> something else in the minibuffer and submit what they typed. >> >> * lisp/minibuffer.el (completion--insert): Add a space before each >> candidate. > > I don't think anyone would like such a space shifting the whole layout > to the right. Rather I'd recommend to use a space after each candidate. > There is already a space between candidates. Only at the end a space is > missing. > > Or without adding a space at the end we could change `choose-completion` > to not select the candidate when point is at the end Oh, yes, I definitely like the idea of not submitting the candidate when point is at the end, no need for any extra space. This would work well with my thought about having M-RET to submitting even a "deselected" candidate: the new behavior of not submitting a candidate when point is at the end would only be active for the new command minibuffer-choose-completion-or-exit. > (`choose-completion` needs fixing anyway since currently it raises an > error at the end of the first completion in case of no header.) > > This still won't solve the case of no header. So in this case > for the initial position we could add a narrow line at the top: > > (propertize "\n" 'face '(:height 0)) > > This solves a lot of problems, and will help to remove the complicated > special-handling of the 'first-completion' text property in many places. This seems fine to me, but as Eli points out, terminal users probably won't like the extra "wasted" line. Maybe if we're in the terminal and there's no header, we could add a single space before the first completion? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-19 14:41 ` Spencer Baugh @ 2023-11-19 18:01 ` Juri Linkov 2023-11-19 19:41 ` Spencer Baugh 2023-11-20 2:58 ` Spencer Baugh 2023-11-23 13:39 ` sbaugh 1 sibling, 2 replies; 62+ messages in thread From: Juri Linkov @ 2023-11-19 18:01 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel >>> It sets completions-auto-update to 'deselect by default, which I think >>> is reasonable? >> >> Isn't deselection needed only when minibuffer-visible-completions is enabled? > > I think we could provide some nice consistency by making it always > active. > > As part of this change, I think we should make sure M-RET will submit a > completion candidate even if it's been "deselected". That would be nice > because then M-RET serves a useful purpose with > minibuffer-visible-completions=t: you can submit the previously-selected > completion candidate even if you've typed (causing deselection) since > selecting it. > > With that M-RET behavior, completions-auto-update='deselect doesn't > change behavior from Emacs 29, so I think it's a plausible default. Please note that currently there is no need to use M-RET by default because the default value of minibuffer-completion-auto-choose is t, and M-down inserts the candidate that is accepted by RET. So M-RET could help only in case of minibuffer-visible-completions=t when the editing deselected a completion candidate. > And if we do have completions-auto-update='deselect by default, then > perhaps we can consider another change to the defaults: make RET always > submit the selected completion candidate. That would actually change > behavior, since M-<down> followed *immediately* by RET would submit the > selected completion candidate, but maybe it's worth it? I doubt that any change of the default behavior would be acceptable. >>> * lisp/minibuffer.el (completion--insert): Add a space before each >>> candidate. >> >> I don't think anyone would like such a space shifting the whole layout >> to the right. Rather I'd recommend to use a space after each candidate. >> There is already a space between candidates. Only at the end a space is >> missing. >> >> Or without adding a space at the end we could change `choose-completion` >> to not select the candidate when point is at the end > > Oh, yes, I definitely like the idea of not submitting the candidate when > point is at the end, no need for any extra space. This would work well > with my thought about having M-RET to submitting even a "deselected" > candidate: the new behavior of not submitting a candidate when point is > at the end would only be active for the new command > minibuffer-choose-completion-or-exit. Agreed, so M-RET will override the logic of RET. >> (`choose-completion` needs fixing anyway since currently it raises an >> error at the end of the first completion in case of no header.) >> >> This still won't solve the case of no header. So in this case >> for the initial position we could add a narrow line at the top: >> >> (propertize "\n" 'face '(:height 0)) >> >> This solves a lot of problems, and will help to remove the complicated >> special-handling of the 'first-completion' text property in many places. > > This seems fine to me, but as Eli points out, terminal users probably > won't like the extra "wasted" line. Maybe if we're in the terminal and > there's no header, we could add a single space before the first > completion? A single space the first completion will break the vertical alignment. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-19 18:01 ` Juri Linkov @ 2023-11-19 19:41 ` Spencer Baugh 2023-11-20 2:58 ` Spencer Baugh 1 sibling, 0 replies; 62+ messages in thread From: Spencer Baugh @ 2023-11-19 19:41 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>> This still won't solve the case of no header. So in this case >>> for the initial position we could add a narrow line at the top: >>> >>> (propertize "\n" 'face '(:height 0)) >>> >>> This solves a lot of problems, and will help to remove the complicated >>> special-handling of the 'first-completion' text property in many places. >> >> This seems fine to me, but as Eli points out, terminal users probably >> won't like the extra "wasted" line. Maybe if we're in the terminal and >> there's no header, we could add a single space before the first >> completion? > > A single space the first completion will break the vertical alignment. This is true, but inserting an empty newline will basically counteract completions-header-format=nil. (Perhaps that shouldn't have been customizable in the first place...) ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-19 18:01 ` Juri Linkov 2023-11-19 19:41 ` Spencer Baugh @ 2023-11-20 2:58 ` Spencer Baugh 1 sibling, 0 replies; 62+ messages in thread From: Spencer Baugh @ 2023-11-20 2:58 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel Juri Linkov <juri@linkov.net> writes: >>>> It sets completions-auto-update to 'deselect by default, which I think >>>> is reasonable? >>> >>> Isn't deselection needed only when minibuffer-visible-completions is enabled? >> >> I think we could provide some nice consistency by making it always >> active. >> >> As part of this change, I think we should make sure M-RET will submit a >> completion candidate even if it's been "deselected". That would be nice >> because then M-RET serves a useful purpose with >> minibuffer-visible-completions=t: you can submit the previously-selected >> completion candidate even if you've typed (causing deselection) since >> selecting it. >> >> With that M-RET behavior, completions-auto-update='deselect doesn't >> change behavior from Emacs 29, so I think it's a plausible default. > > Please note that currently there is no need to use M-RET by default > because the default value of minibuffer-completion-auto-choose is t, > and M-down inserts the candidate that is accepted by RET. > > So M-RET could help only in case of minibuffer-visible-completions=t > when the editing deselected a completion candidate. That is true. But M-RET is still important for completion-in-region. In any case, deselecting by default doesn't change behavior for any of: - completion-in-region - minibuffer completion with minibuffer-completion-auto-choose=t - minibuffer completion with minibuffer-completion-auto-choose=nil So I think deselecting by default is a backwards-compatible change. >> And if we do have completions-auto-update='deselect by default, then >> perhaps we can consider another change to the defaults: make RET always >> submit the selected completion candidate. That would actually change >> behavior, since M-<down> followed *immediately* by RET would submit the >> selected completion candidate, but maybe it's worth it? > > I doubt that any change of the default behavior would be acceptable. Well, M-<down>/M-<up> have only been present in one version of Emacs, so a change to their default behavior, which supports an opt-out, seems possible if it's a real improvement. But anyway, this is better to discuss once we've settled on the design of the deselection behavior. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-19 14:41 ` Spencer Baugh 2023-11-19 18:01 ` Juri Linkov @ 2023-11-23 13:39 ` sbaugh 2023-11-24 7:54 ` Juri Linkov 1 sibling, 1 reply; 62+ messages in thread From: sbaugh @ 2023-11-23 13:39 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1222 bytes --] Spencer Baugh <sbaugh@catern.com> writes: >>> minibuffer-visible-completions makes RET submit the selected >>> completion candidate, if any, ignoring the contents of the minibuffer. >>> But a user might select a completion candidate and then want to type >>> something else in the minibuffer and submit what they typed. >>> >>> * lisp/minibuffer.el (completion--insert): Add a space before each >>> candidate. >> >> I don't think anyone would like such a space shifting the whole layout >> to the right. Rather I'd recommend to use a space after each candidate. >> There is already a space between candidates. Only at the end a space is >> missing. >> >> Or without adding a space at the end we could change `choose-completion` >> to not select the candidate when point is at the end > > Oh, yes, I definitely like the idea of not submitting the candidate when > point is at the end, no need for any extra space. This would work well > with my thought about having M-RET to submitting even a "deselected" > candidate: the new behavior of not submitting a candidate when point is > at the end would only be active for the new command > minibuffer-choose-completion-or-exit. Here's an update patch with this approach. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Deselect-the-selected-completion-candidate-when-typi.patch --] [-- Type: text/x-patch, Size: 7738 bytes --] From 19344d19df371a7432ceb74f66c0f7ece9b4eed0 Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh@catern.com> Date: Thu, 23 Nov 2023 13:37:29 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-choose-completion-or-exit submits the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate so that minibuffer-choose-completion-or-exit will not choose it. minibuffer-choose-completion has the same behavior as before, and is not affected by the deselection. * lisp/minibuffer.el (completions-auto-update, completions--deselect) (completions--update-if-displayed, completions--after-change) (minibuffer--old-point, completions--post-command): Add. (minibuffer-completion-help): Add completions--after-change and completions--post-command as hooks. (minibuffer-next-completion): Bind completions-auto-update to nil to avoid immediately deselecting the completion. (minibuffer-choose-completion-or-exit): Bind choose-completion-deselect-if-after so deselection takes effect. * lisp/simple.el (choose-completion-deselect-if-after): Add. (completions--get-posn): Check choose-completion-deselect-if-after. --- lisp/minibuffer.el | 57 ++++++++++++++++++++++++++++++++++++++++++++-- lisp/simple.el | 39 ++++++++++++++++++------------- 2 files changed, 78 insertions(+), 18 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index dd29c155fd3..990a69b35bb 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2440,6 +2440,52 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completions-auto-update 'deselect + "If non-nil, change the *Completions* buffer as you type. + +If `deselect', if a completion candidate in *Completions* is +selected (point is on it), it will be deselected (point will be +moved just before it) when the minibuffer point or contents +change. + +This only affects the *Completions* buffer if it is already +displayed." + :type '(choice (const :tag "*Completions* doesn't change as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" deselect)) + :version "30.1") + +(defun completions--deselect () + "If in a completion candidate, move to just after the end of it. + +The candidate will still be chosen by `choose-completion' unless +`choose-completion-deselect-if-after' is non-nil." + (when (get-text-property (point) 'mouse-face) + (goto-char (or (next-single-property-change (point) 'mouse-face) + (point-max))))) + +(defun completions--update-if-displayed () + "Update a displayed *Completions* buffer based on `completions-auto-update'" + (when completions-auto-update + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (when (eq completions-auto-update 'deselect) + (completions--deselect)))))) + +(defun completions--after-change () + "Update displayed *Completions* buffer after change in minibuffer contents." + (when (minibufferp) + (completions--update-if-displayed))) + +(defvar-local minibuffer--old-point nil) + +(defun completions--post-command () + "Update displayed *Completions* buffer after change in minibuffer point." + (when (and (minibufferp) (not (eq minibuffer--old-point (point)))) + (setq minibuffer--old-point (point)) + (unless (and completions-auto-update + (memq this-command '(minibuffer-next-completion minibuffer-previous-completion))) + (completions--update-if-displayed)))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2462,6 +2508,8 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'post-command-hook #'completions--post-command t) + (remove-hook 'after-change-hook #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2526,6 +2574,9 @@ minibuffer-completion-help (body-function . ,#'(lambda (window) (with-current-buffer mainbuf + (when completions-auto-update + (add-hook 'post-command-hook #'completions--post-command nil t) + (add-hook 'after-change-hook #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4739,7 +4790,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completions-auto-update nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) @@ -4787,7 +4839,8 @@ minibuffer-choose-completion-or-exit contents." (interactive "P") (condition-case nil - (minibuffer-choose-completion no-exit no-quit) + (let ((choose-completion-deselect-if-after t)) + (minibuffer-choose-completion no-exit no-quit)) (error (minibuffer-complete-and-exit)))) (defun minibuffer-complete-history () diff --git a/lisp/simple.el b/lisp/simple.el index 34205a223a7..2043233738a 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10096,24 +10096,31 @@ next-line-completion (if pos (goto-char pos)))) (setq n (1+ n))))) +(defvar choose-completion-deselect-if-after nil + "If non-nil, don't choose a completion candidate if point is right after it. + +This makes `completions--deselect' effective.") + (defun completions--get-posn (position) "Return the completion at POSITION as a string." - (save-excursion - (goto-char position) - (let (beg) - (cond - ((and (not (eobp)) - (get-text-property (point) 'completion--string)) - (setq beg (1+ (point)))) - ((and (not (bobp)) - (get-text-property (1- (point)) 'completion--string)) - (setq beg (point)))) - (when beg - (setq beg (or (previous-single-property-change - beg 'completion--string) - beg)) - (substring-no-properties - (get-text-property beg 'completion--string)))))) + (if choose-completion-deselect-if-after + (substring-no-properties (get-text-property position 'completion--string)) + (save-excursion + (goto-char position) + (let (beg) + (cond + ((and (not (eobp)) + (get-text-property (point) 'completion--string)) + (setq beg (1+ (point)))) + ((and (not (bobp)) + (get-text-property (1- (point)) 'completion--string)) + (setq beg (point)))) + (when beg + (setq beg (or (previous-single-property-change + beg 'completion--string) + beg)) + (substring-no-properties + (get-text-property beg 'completion--string))))))) (defun choose-completion (&optional event no-exit no-quit) "Choose the completion at point. -- 2.42.1 ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-23 13:39 ` sbaugh @ 2023-11-24 7:54 ` Juri Linkov 2023-11-25 15:19 ` Spencer Baugh 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-24 7:54 UTC (permalink / raw) To: sbaugh; +Cc: emacs-devel > Here's an update patch with this approach. Sorry, the patch can't be applied, there is no existing completions--get-posn to change: > +This makes `completions--deselect' effective.") > + > (defun completions--get-posn (position) > "Return the completion at POSITION as a string." > - (save-excursion > - (goto-char position) ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-24 7:54 ` Juri Linkov @ 2023-11-25 15:19 ` Spencer Baugh 2023-11-25 16:08 ` Eli Zaretskii 2023-11-25 17:46 ` Juri Linkov 0 siblings, 2 replies; 62+ messages in thread From: Spencer Baugh @ 2023-11-25 15:19 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 443 bytes --] Juri Linkov <juri@linkov.net> writes: >> Here's an update patch with this approach. > > Sorry, the patch can't be applied, there is no existing > completions--get-posn to change: > >> +This makes `completions--deselect' effective.") >> + >> (defun completions--get-posn (position) >> "Return the completion at POSITION as a string." >> - (save-excursion >> - (goto-char position) Oops, here it is again rebased directly onto master. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Deselect-the-selected-completion-candidate-when-typi.patch --] [-- Type: text/x-patch, Size: 7291 bytes --] From 874b455870ffd6057fd63e353240a91a742b3c2d Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh@catern.com> Date: Thu, 23 Nov 2023 13:37:29 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-choose-completion-or-exit submits the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate so that minibuffer-choose-completion-or-exit will not choose it. minibuffer-choose-completion has the same behavior as before, and is not affected by the deselection. * lisp/minibuffer.el (completions-auto-update, completions--deselect) (completions--update-if-displayed, completions--after-change) (minibuffer--old-point, completions--post-command): Add. (minibuffer-completion-help): Add completions--after-change and completions--post-command as hooks. (minibuffer-next-completion): Bind completions-auto-update to nil to avoid immediately deselecting the completion. (minibuffer-choose-completion-or-exit): Bind choose-completion-deselect-if-after so deselection takes effect. * lisp/simple.el (choose-completion-deselect-if-after): Add. (completions--get-posn): Check choose-completion-deselect-if-after. --- lisp/minibuffer.el | 57 ++++++++++++++++++++++++++++++++++++++++++++-- lisp/simple.el | 10 +++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5c12d9fc914..a6acd1e999b 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2378,6 +2378,52 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completions-auto-update 'deselect + "If non-nil, change the *Completions* buffer as you type. + +If `deselect', if a completion candidate in *Completions* is +selected (point is on it), it will be deselected (point will be +moved just before it) when the minibuffer point or contents +change. + +This only affects the *Completions* buffer if it is already +displayed." + :type '(choice (const :tag "*Completions* doesn't change as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" deselect)) + :version "30.1") + +(defun completions--deselect () + "If in a completion candidate, move to just after the end of it. + +The candidate will still be chosen by `choose-completion' unless +`choose-completion-deselect-if-after' is non-nil." + (when (get-text-property (point) 'mouse-face) + (goto-char (or (next-single-property-change (point) 'mouse-face) + (point-max))))) + +(defun completions--update-if-displayed () + "Update a displayed *Completions* buffer based on `completions-auto-update'" + (when completions-auto-update + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (when (eq completions-auto-update 'deselect) + (completions--deselect)))))) + +(defun completions--after-change () + "Update displayed *Completions* buffer after change in minibuffer contents." + (when (minibufferp) + (completions--update-if-displayed))) + +(defvar-local minibuffer--old-point nil) + +(defun completions--post-command () + "Update displayed *Completions* buffer after change in minibuffer point." + (when (and (minibufferp) (not (eq minibuffer--old-point (point)))) + (setq minibuffer--old-point (point)) + (unless (and completions-auto-update + (memq this-command '(minibuffer-next-completion minibuffer-previous-completion))) + (completions--update-if-displayed)))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2400,6 +2446,8 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'post-command-hook #'completions--post-command t) + (remove-hook 'after-change-hook #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2460,6 +2508,9 @@ minibuffer-completion-help (body-function . ,#'(lambda (_window) (with-current-buffer mainbuf + (when completions-auto-update + (add-hook 'post-command-hook #'completions--post-command nil t) + (add-hook 'after-change-hook #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4673,7 +4724,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completions-auto-update nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) @@ -4721,7 +4773,8 @@ minibuffer-choose-completion-or-exit contents." (interactive "P") (condition-case nil - (minibuffer-choose-completion no-exit no-quit) + (let ((choose-completion-deselect-if-after t)) + (minibuffer-choose-completion no-exit no-quit)) (error (minibuffer-complete-and-exit)))) (defun minibuffer-complete-history () diff --git a/lisp/simple.el b/lisp/simple.el index 02c68912dba..791b7dddedc 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10094,6 +10094,11 @@ next-line-completion (if pos (goto-char pos)))) (setq n (1+ n))))) +(defvar choose-completion-deselect-if-after nil + "If non-nil, don't choose a completion candidate if point is right after it. + +This makes `completions--deselect' effective.") + (defun choose-completion (&optional event no-exit no-quit) "Choose the completion at point. If EVENT, use EVENT's position to determine the starting position. @@ -10114,6 +10119,9 @@ choose-completion (insert-function completion-list-insert-choice-function) (completion-no-auto-exit (if no-exit t completion-no-auto-exit)) (choice + (if choose-completion-deselect-if-after + (substring-no-properties + (get-text-property (posn-point (event-start event)) 'completion--string)) (save-excursion (goto-char (posn-point (event-start event))) (let (beg) @@ -10129,7 +10137,7 @@ choose-completion beg 'completion--string) beg)) (substring-no-properties - (get-text-property beg 'completion--string)))))) + (get-text-property beg 'completion--string))))))) (unless (buffer-live-p buffer) (error "Destination buffer is dead")) -- 2.42.1 ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-25 15:19 ` Spencer Baugh @ 2023-11-25 16:08 ` Eli Zaretskii 2023-11-25 18:23 ` sbaugh 2023-11-25 17:46 ` Juri Linkov 1 sibling, 1 reply; 62+ messages in thread From: Eli Zaretskii @ 2023-11-25 16:08 UTC (permalink / raw) To: Spencer Baugh; +Cc: juri, emacs-devel > From: Spencer Baugh <sbaugh@catern.com> > Date: Sat, 25 Nov 2023 15:19:37 +0000 (UTC) > Cc: emacs-devel@gnu.org > > +(defcustom completions-auto-update 'deselect > + "If non-nil, change the *Completions* buffer as you type. ^^^^^^ "Update", not "change". > +If `deselect', if a completion candidate in *Completions* is > +selected (point is on it), it will be deselected (point will be > +moved just before it) when the minibuffer point or contents > +change. This has several issues. First, "If `deselect'" is sub-optimal; it is better to say The value of `deselect' means that ... The double "if" ("If `deselect', if a completion candidate...") is also sub-optimal. At the very least, if you need to say something like that, say If this, then if that, ... Finally, I'm guessing that the value `deselect' means to deslect the candidate _in_addition_to_ update "*Completions*" as the user types. If my guess is correct, please say The value of `deselect' means that in addition to updating *Completions*, the selected candidate will be deselected... > +This only affects the *Completions* buffer if it is already > +displayed." Not sure what this sentence adds. What will happen if you drop it? > + :type '(choice (const :tag "*Completions* doesn't change as you type" nil) > + (const :tag "Typing deselects any completion candidate in *Completions*" deselect)) And now I see that my guess above perhaps was wrong: there's no value t in the choice? Then why not make this a simple boolean? > +(defun completions--deselect () > + "If in a completion candidate, move to just after the end of it. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "If point is at a completion candidate, ..." > +(defun completions--post-command () > + "Update displayed *Completions* buffer after change in minibuffer point." > + (when (and (minibufferp) (not (eq minibuffer--old-point (point)))) > + (setq minibuffer--old-point (point)) > + (unless (and completions-auto-update > + (memq this-command '(minibuffer-next-completion minibuffer-previous-completion))) > + (completions--update-if-displayed)))) Ugh, another post-command hook... It is small wonder users complain that Emacs is slow to respond and feels sluggish. What will happen if the user types fast? Could there be a design that doesn't use post-command-hook? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-25 16:08 ` Eli Zaretskii @ 2023-11-25 18:23 ` sbaugh 2023-11-25 18:48 ` Juri Linkov 2023-11-25 19:00 ` Eli Zaretskii 0 siblings, 2 replies; 62+ messages in thread From: sbaugh @ 2023-11-25 18:23 UTC (permalink / raw) To: Eli Zaretskii; +Cc: juri, emacs-devel [-- Attachment #1: Type: text/plain, Size: 3834 bytes --] Eli Zaretskii <eliz@gnu.org> writes: >> From: Spencer Baugh <sbaugh@catern.com> >> Date: Sat, 25 Nov 2023 15:19:37 +0000 (UTC) >> Cc: emacs-devel@gnu.org >> >> +(defcustom completions-auto-update 'deselect >> + "If non-nil, change the *Completions* buffer as you type. > ^^^^^^ > "Update", not "change". Fixed. >> +If `deselect', if a completion candidate in *Completions* is >> +selected (point is on it), it will be deselected (point will be >> +moved just before it) when the minibuffer point or contents >> +change. > > This has several issues. > > First, "If `deselect'" is sub-optimal; it is better to say > > The value of `deselect' means that ... > > The double "if" ("If `deselect', if a completion candidate...") is > also sub-optimal. At the very least, if you need to say something > like that, say > > If this, then if that, ... Fixed by rewriting the docstring. > Finally, I'm guessing that the value `deselect' means to deslect the > candidate _in_addition_to_ update "*Completions*" as the user types. > If my guess is correct, please say > > The value of `deselect' means that in addition to updating > *Completions*, the selected candidate will be deselected... > >> +This only affects the *Completions* buffer if it is already >> +displayed." > > Not sure what this sentence adds. What will happen if you drop it? Dropped. >> + :type '(choice (const :tag "*Completions* doesn't change as you type" nil) >> + (const :tag "Typing deselects any completion candidate in *Completions*" deselect)) > > And now I see that my guess above perhaps was wrong: there's no value > t in the choice? Then why not make this a simple boolean? I want to add another value later to support actually updating *Completions* as the user types. That value will include the current 'deselect behavior, but do even more, using the same machinery. By just having one option we avoid proliferation of completion-related options. Actually, though, I think updating *Completions* doesn't have to mean deselecting the currently selected candidate. It might make sense to support completions-auto-deselect=nil and completions-auto-update=t. So a separate option makes sense. So I just change this option to be completions-auto-deselect, which is specific to deselecting on typing. >> +(defun completions--deselect () >> + "If in a completion candidate, move to just after the end of it. > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > "If point is at a completion candidate, ..." > >> +(defun completions--post-command () >> + "Update displayed *Completions* buffer after change in minibuffer point." >> + (when (and (minibufferp) (not (eq minibuffer--old-point (point)))) >> + (setq minibuffer--old-point (point)) >> + (unless (and completions-auto-update >> + (memq this-command '(minibuffer-next-completion minibuffer-previous-completion))) >> + (completions--update-if-displayed)))) > > Ugh, another post-command hook... It is small wonder users complain > that Emacs is slow to respond and feels sluggish. What will happen if > the user types fast? Could there be a design that doesn't use > post-command-hook? Yes, actually, this can be dropped for now. We only really need the after-change-based updating. The post-command-hook only serves to deselect the completion candidate when the user moves point. But that's probably not necessary and maybe even undesirable. The core of all of this is that we need to update what's displayed in a different buffer (*Completions*) based on what the user is doing and typing in one buffer (the minibuffer). So I think a hook-based design is our only option? (Maybe redisplay of *Completions* could check if the first buffer has changed and modify it?) Anyway, dropped the post-command-hook. Fixed patch: [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Deselect-the-selected-completion-candidate-when-typi.patch --] [-- Type: text/x-patch, Size: 6325 bytes --] From ece9fefeb267a24a7a09b1828104b619a62ea2a7 Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh@catern.com> Date: Thu, 23 Nov 2023 13:37:29 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-choose-completion-or-exit submits the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate so that minibuffer-choose-completion-or-exit will not choose it. minibuffer-choose-completion has the same behavior as before, and is not affected by the deselection. * lisp/minibuffer.el (completions-auto-deselect, completions--deselect) (completions--after-change): Add. (minibuffer-completion-help): Add completions--after-change hook. (minibuffer-next-completion): Bind completions-auto-deselect to nil to avoid immediately deselecting the completion. (minibuffer-choose-completion-or-exit): Bind choose-completion-deselect-if-after so deselection takes effect. * lisp/simple.el (choose-completion-deselect-if-after): Add. (choose-completion): Check choose-completion-deselect-if-after. --- lisp/minibuffer.el | 36 ++++++++++++++++++++++++++++++++++-- lisp/simple.el | 10 +++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5c12d9fc914..69483ad2aa4 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2378,6 +2378,33 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completions-auto-deselect t + "If non-nil, deselect the selected completion candidate when you type. + +A non-nil value means that after typing, point in *Completions* +will be moved off any completion candidates. This means +`minibuffer-choose-completion-or-exit' will exit with the +minibuffer's current contents, instead of a completion candidate." + :type '(choice (const :tag "Candidates in *Completions* stay selected as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" t)) + :version "30.1") + +(defun completions--deselect () + "If point is in a completion candidate, move to just after the end of it. + +The candidate will still be chosen by `choose-completion' unless +`choose-completion-deselect-if-after' is non-nil." + (when (get-text-property (point) 'mouse-face) + (goto-char (or (next-single-property-change (point) 'mouse-face) + (point-max))))) + +(defun completions--after-change (_start _end _old-len) + "Update displayed *Completions* buffer after change in buffer contents." + (when completions-auto-deselect + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (completions--deselect))))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2400,6 +2427,7 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'after-change-functions #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2460,6 +2488,8 @@ minibuffer-completion-help (body-function . ,#'(lambda (_window) (with-current-buffer mainbuf + (when completions-auto-deselect + (add-hook 'after-change-functions #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4673,7 +4703,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completions-auto-deselect nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) @@ -4721,7 +4752,8 @@ minibuffer-choose-completion-or-exit contents." (interactive "P") (condition-case nil - (minibuffer-choose-completion no-exit no-quit) + (let ((choose-completion-deselect-if-after t)) + (minibuffer-choose-completion no-exit no-quit)) (error (minibuffer-complete-and-exit)))) (defun minibuffer-complete-history () diff --git a/lisp/simple.el b/lisp/simple.el index 02c68912dba..791b7dddedc 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10094,6 +10094,11 @@ next-line-completion (if pos (goto-char pos)))) (setq n (1+ n))))) +(defvar choose-completion-deselect-if-after nil + "If non-nil, don't choose a completion candidate if point is right after it. + +This makes `completions--deselect' effective.") + (defun choose-completion (&optional event no-exit no-quit) "Choose the completion at point. If EVENT, use EVENT's position to determine the starting position. @@ -10114,6 +10119,9 @@ choose-completion (insert-function completion-list-insert-choice-function) (completion-no-auto-exit (if no-exit t completion-no-auto-exit)) (choice + (if choose-completion-deselect-if-after + (substring-no-properties + (get-text-property (posn-point (event-start event)) 'completion--string)) (save-excursion (goto-char (posn-point (event-start event))) (let (beg) @@ -10129,7 +10137,7 @@ choose-completion beg 'completion--string) beg)) (substring-no-properties - (get-text-property beg 'completion--string)))))) + (get-text-property beg 'completion--string))))))) (unless (buffer-live-p buffer) (error "Destination buffer is dead")) -- 2.42.1 ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-25 18:23 ` sbaugh @ 2023-11-25 18:48 ` Juri Linkov 2023-11-26 13:10 ` sbaugh 2023-11-25 19:00 ` Eli Zaretskii 1 sibling, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-25 18:48 UTC (permalink / raw) To: sbaugh; +Cc: Eli Zaretskii, emacs-devel > +(defcustom completions-auto-deselect t BTW, all related options have the same prefix `completion-auto-', i.e. singular when related to the minibuffer, and plural when used only for the *Completions* buffer. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-25 18:48 ` Juri Linkov @ 2023-11-26 13:10 ` sbaugh 0 siblings, 0 replies; 62+ messages in thread From: sbaugh @ 2023-11-26 13:10 UTC (permalink / raw) To: Juri Linkov; +Cc: Eli Zaretskii, emacs-devel [-- Attachment #1: Type: text/plain, Size: 285 bytes --] Juri Linkov <juri@linkov.net> writes: >> +(defcustom completions-auto-deselect t > > BTW, all related options have the same prefix `completion-auto-', > i.e. singular when related to the minibuffer, and plural > when used only for the *Completions* buffer. Renamed for consistency. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Deselect-the-selected-completion-candidate-when-typi.patch --] [-- Type: text/x-patch, Size: 6319 bytes --] From 8d97da6d1a4247407f508b2b37da3d49ee839135 Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh@catern.com> Date: Thu, 23 Nov 2023 13:37:29 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-choose-completion-or-exit submits the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate so that minibuffer-choose-completion-or-exit will not choose it. minibuffer-choose-completion has the same behavior as before, and is not affected by the deselection. * lisp/minibuffer.el (completion-auto-deselect, completions--deselect) (completions--after-change): Add. (minibuffer-completion-help): Add completions--after-change hook. (minibuffer-next-completion): Bind completion-auto-deselect to nil to avoid immediately deselecting the completion. (minibuffer-choose-completion-or-exit): Bind choose-completion-deselect-if-after so deselection takes effect. * lisp/simple.el (choose-completion-deselect-if-after): Add. (choose-completion): Check choose-completion-deselect-if-after. --- lisp/minibuffer.el | 36 ++++++++++++++++++++++++++++++++++-- lisp/simple.el | 10 +++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5c12d9fc914..841d29d12de 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2378,6 +2378,33 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completion-auto-deselect t + "If non-nil, deselect the selected completion candidate when you type. + +A non-nil value means that after typing, point in *Completions* +will be moved off any completion candidates. This means +`minibuffer-choose-completion-or-exit' will exit with the +minibuffer's current contents, instead of a completion candidate." + :type '(choice (const :tag "Candidates in *Completions* stay selected as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" t)) + :version "30.1") + +(defun completions--deselect () + "If point is in a completion candidate, move to just after the end of it. + +The candidate will still be chosen by `choose-completion' unless +`choose-completion-deselect-if-after' is non-nil." + (when (get-text-property (point) 'mouse-face) + (goto-char (or (next-single-property-change (point) 'mouse-face) + (point-max))))) + +(defun completions--after-change (_start _end _old-len) + "Update displayed *Completions* buffer after change in buffer contents." + (when completion-auto-deselect + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (completions--deselect))))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2400,6 +2427,7 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'after-change-functions #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2460,6 +2488,8 @@ minibuffer-completion-help (body-function . ,#'(lambda (_window) (with-current-buffer mainbuf + (when completion-auto-deselect + (add-hook 'after-change-functions #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4673,7 +4703,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completion-auto-deselect nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) @@ -4721,7 +4752,8 @@ minibuffer-choose-completion-or-exit contents." (interactive "P") (condition-case nil - (minibuffer-choose-completion no-exit no-quit) + (let ((choose-completion-deselect-if-after t)) + (minibuffer-choose-completion no-exit no-quit)) (error (minibuffer-complete-and-exit)))) (defun minibuffer-complete-history () diff --git a/lisp/simple.el b/lisp/simple.el index 02c68912dba..791b7dddedc 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10094,6 +10094,11 @@ next-line-completion (if pos (goto-char pos)))) (setq n (1+ n))))) +(defvar choose-completion-deselect-if-after nil + "If non-nil, don't choose a completion candidate if point is right after it. + +This makes `completions--deselect' effective.") + (defun choose-completion (&optional event no-exit no-quit) "Choose the completion at point. If EVENT, use EVENT's position to determine the starting position. @@ -10114,6 +10119,9 @@ choose-completion (insert-function completion-list-insert-choice-function) (completion-no-auto-exit (if no-exit t completion-no-auto-exit)) (choice + (if choose-completion-deselect-if-after + (substring-no-properties + (get-text-property (posn-point (event-start event)) 'completion--string)) (save-excursion (goto-char (posn-point (event-start event))) (let (beg) @@ -10129,7 +10137,7 @@ choose-completion beg 'completion--string) beg)) (substring-no-properties - (get-text-property beg 'completion--string)))))) + (get-text-property beg 'completion--string))))))) (unless (buffer-live-p buffer) (error "Destination buffer is dead")) -- 2.42.1 ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-25 18:23 ` sbaugh 2023-11-25 18:48 ` Juri Linkov @ 2023-11-25 19:00 ` Eli Zaretskii 1 sibling, 0 replies; 62+ messages in thread From: Eli Zaretskii @ 2023-11-25 19:00 UTC (permalink / raw) To: sbaugh; +Cc: juri, emacs-devel > From: sbaugh@catern.com > Date: Sat, 25 Nov 2023 18:23:25 +0000 (UTC) > Cc: juri@linkov.net, emacs-devel@gnu.org > > > Ugh, another post-command hook... It is small wonder users complain > > that Emacs is slow to respond and feels sluggish. What will happen if > > the user types fast? Could there be a design that doesn't use > > post-command-hook? > > Yes, actually, this can be dropped for now. We only really need the > after-change-based updating. > > The post-command-hook only serves to deselect the completion candidate > when the user moves point. But that's probably not necessary and maybe > even undesirable. > > The core of all of this is that we need to update what's displayed in a > different buffer (*Completions*) based on what the user is doing and > typing in one buffer (the minibuffer). So I think a hook-based design > is our only option? No, you could use an idle timer as well. The advantage is that if the user is typing fast, an idle timer based design will not make Emacs less responsive. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-25 15:19 ` Spencer Baugh 2023-11-25 16:08 ` Eli Zaretskii @ 2023-11-25 17:46 ` Juri Linkov 2023-11-26 14:33 ` Spencer Baugh 1 sibling, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-25 17:46 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel > minibuffer-choose-completion-or-exit submits the selected completion > candidate, if any, ignoring the contents of the minibuffer. But a > user might select a completion candidate and then want to type > something else in the minibuffer and submit what they typed. Thanks, everything works nicely now except the special case of completion-show-help=nil and completions-header-format=nil. Initially the first completion is deselected, and <down> selects it. But when the first key typed is M-<down> the second completion is selected instead of the first because M-<down> moves to the second column when point is between the first and second completions. A possible workaround would be to check the special property 'first-completion'. But then when the logic uses text properties, why not to use more text properties that indicate that a candidate is deselected (and remove highlighting in this case) instead of moving point somewhere outside of the candidate? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-25 17:46 ` Juri Linkov @ 2023-11-26 14:33 ` Spencer Baugh 2023-11-27 7:22 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-26 14:33 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1634 bytes --] Juri Linkov <juri@linkov.net> writes: >> minibuffer-choose-completion-or-exit submits the selected completion >> candidate, if any, ignoring the contents of the minibuffer. But a >> user might select a completion candidate and then want to type >> something else in the minibuffer and submit what they typed. > > Thanks, everything works nicely now except the special case > of completion-show-help=nil and completions-header-format=nil. > > Initially the first completion is deselected, and <down> selects it. > But when the first key typed is M-<down> the second completion > is selected instead of the first because M-<down> moves to the > second column when point is between the first and second completions. I was thinking that issue is unrelated to this patch and so it could be fixed separately. But actually I guess this patch makes it more visible indeed, so I guess we need to fix it now. I think the "add a thin newline" approach is totally fine. I did that in my attached patch. > A possible workaround would be to check the special property > 'first-completion'. But then when the logic uses text properties, > why not to use more text properties that indicate that a candidate > is deselected (and remove highlighting in this case) instead of > moving point somewhere outside of the candidate? Even if we work around this with text properties, I think it's nice for deselection to be usually determined by location of point. I think that's easier to understand for the user - they should be able to deselect or reselect something purely by moving point around, it shouldn't require text property manipulation. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Deselect-the-selected-completion-candidate-when-typi.patch --] [-- Type: text/x-patch, Size: 7133 bytes --] From f4e2bdda0e30af933bee896aa7b5ca2b22684ab5 Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh@catern.com> Date: Thu, 23 Nov 2023 13:37:29 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-choose-completion-or-exit submits the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate so that minibuffer-choose-completion-or-exit will not choose it. minibuffer-choose-completion has the same behavior as before, and is not affected by the deselection. * lisp/minibuffer.el (completion-auto-deselect, completions--deselect) (completions--after-change): Add. (minibuffer-completion-help): Add completions--after-change hook. (minibuffer-next-completion): Bind completion-auto-deselect to nil to avoid immediately deselecting the completion. (minibuffer-choose-completion-or-exit): Bind choose-completion-deselect-if-after so deselection takes effect. (display-completion-list): Guarantee a newline at the beginning of *Completions* to avoid ambiguity about candidate selection. * lisp/simple.el (choose-completion-deselect-if-after): Add. (choose-completion): Check choose-completion-deselect-if-after. --- lisp/minibuffer.el | 43 +++++++++++++++++++++++++++++++++++++++---- lisp/simple.el | 11 ++++++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5c12d9fc914..303170125ff 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2310,8 +2310,11 @@ display-completion-list (with-current-buffer standard-output (goto-char (point-max)) - (when completions-header-format - (insert (format completions-header-format (length completions)))) + (if completions-header-format + (insert (format completions-header-format (length completions))) + (unless completion-show-help + ;; Ensure beginning-of-buffer isn't a completion. + (insert (propertize "\n" 'face '(:height 0))))) (completion--insert-strings completions group-fun))) (run-hooks 'completion-setup-hook) @@ -2378,6 +2381,33 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completion-auto-deselect t + "If non-nil, deselect the selected completion candidate when you type. + +A non-nil value means that after typing, point in *Completions* +will be moved off any completion candidates. This means +`minibuffer-choose-completion-or-exit' will exit with the +minibuffer's current contents, instead of a completion candidate." + :type '(choice (const :tag "Candidates in *Completions* stay selected as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" t)) + :version "30.1") + +(defun completions--deselect () + "If point is in a completion candidate, move to just after the end of it. + +The candidate will still be chosen by `choose-completion' unless +`choose-completion-deselect-if-after' is non-nil." + (when (get-text-property (point) 'mouse-face) + (goto-char (or (next-single-property-change (point) 'mouse-face) + (point-max))))) + +(defun completions--after-change (_start _end _old-len) + "Update displayed *Completions* buffer after change in buffer contents." + (when completion-auto-deselect + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (completions--deselect))))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2400,6 +2430,7 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'after-change-functions #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2460,6 +2491,8 @@ minibuffer-completion-help (body-function . ,#'(lambda (_window) (with-current-buffer mainbuf + (when completion-auto-deselect + (add-hook 'after-change-functions #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4673,7 +4706,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completion-auto-deselect nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) @@ -4721,7 +4755,8 @@ minibuffer-choose-completion-or-exit contents." (interactive "P") (condition-case nil - (minibuffer-choose-completion no-exit no-quit) + (let ((choose-completion-deselect-if-after t)) + (minibuffer-choose-completion no-exit no-quit)) (error (minibuffer-complete-and-exit)))) (defun minibuffer-complete-history () diff --git a/lisp/simple.el b/lisp/simple.el index 02c68912dba..bf57b285fca 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10094,6 +10094,11 @@ next-line-completion (if pos (goto-char pos)))) (setq n (1+ n))))) +(defvar choose-completion-deselect-if-after nil + "If non-nil, don't choose a completion candidate if point is right after it. + +This makes `completions--deselect' effective.") + (defun choose-completion (&optional event no-exit no-quit) "Choose the completion at point. If EVENT, use EVENT's position to determine the starting position. @@ -10114,6 +10119,10 @@ choose-completion (insert-function completion-list-insert-choice-function) (completion-no-auto-exit (if no-exit t completion-no-auto-exit)) (choice + (if choose-completion-deselect-if-after + (if-let ((str (get-text-property (posn-point (event-start event)) 'completion--string))) + (substring-no-properties str) + (error "No completion here")) (save-excursion (goto-char (posn-point (event-start event))) (let (beg) @@ -10129,7 +10138,7 @@ choose-completion beg 'completion--string) beg)) (substring-no-properties - (get-text-property beg 'completion--string)))))) + (get-text-property beg 'completion--string))))))) (unless (buffer-live-p buffer) (error "Destination buffer is dead")) -- 2.42.1 ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-26 14:33 ` Spencer Baugh @ 2023-11-27 7:22 ` Juri Linkov 2023-11-28 14:48 ` Spencer Baugh 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-27 7:22 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel >> A possible workaround would be to check the special property >> 'first-completion'. But then when the logic uses text properties, >> why not to use more text properties that indicate that a candidate >> is deselected (and remove highlighting in this case) instead of >> moving point somewhere outside of the candidate? > > Even if we work around this with text properties, I think it's nice for > deselection to be usually determined by location of point. I think > that's easier to understand for the user - they should be able to > deselect or reselect something purely by moving point around, it > shouldn't require text property manipulation. I agree that it's better to indicate deselection by moving point. After more testing I found a small problem: M-x calc TAB TAB When the selected candidate is calc-dispatch (C-x *) then point is moved before the suffix "(C-x *)", and after editing it is selected on RET. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-27 7:22 ` Juri Linkov @ 2023-11-28 14:48 ` Spencer Baugh 2023-11-28 17:23 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-28 14:48 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1119 bytes --] Juri Linkov <juri@linkov.net> writes: >>> A possible workaround would be to check the special property >>> 'first-completion'. But then when the logic uses text properties, >>> why not to use more text properties that indicate that a candidate >>> is deselected (and remove highlighting in this case) instead of >>> moving point somewhere outside of the candidate? >> >> Even if we work around this with text properties, I think it's nice for >> deselection to be usually determined by location of point. I think >> that's easier to understand for the user - they should be able to >> deselect or reselect something purely by moving point around, it >> shouldn't require text property manipulation. > > I agree that it's better to indicate deselection by moving point. > > After more testing I found a small problem: > > M-x calc TAB TAB > > When the selected candidate is > > calc-dispatch (C-x *) > > then point is moved before the suffix "(C-x *)", > and after editing it is selected on RET. Ah, good catch. Fixed (by using 'completion--string to detect whether point is on a completion rather 'mouse-face) [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Deselect-the-selected-completion-candidate-when-typi.patch --] [-- Type: text/x-patch, Size: 7149 bytes --] From 9321015b4000ee1d26788a6b5b2d38d6c248c5d4 Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh@catern.com> Date: Thu, 23 Nov 2023 13:37:29 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-choose-completion-or-exit submits the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate so that minibuffer-choose-completion-or-exit will not choose it. minibuffer-choose-completion has the same behavior as before, and is not affected by the deselection. * lisp/minibuffer.el (completion-auto-deselect, completions--deselect) (completions--after-change): Add. (minibuffer-completion-help): Add completions--after-change hook. (minibuffer-next-completion): Bind completion-auto-deselect to nil to avoid immediately deselecting the completion. (minibuffer-choose-completion-or-exit): Bind choose-completion-deselect-if-after so deselection takes effect. (display-completion-list): Guarantee a newline at the beginning of *Completions* to avoid ambiguity about candidate selection. * lisp/simple.el (choose-completion-deselect-if-after): Add. (choose-completion): Check choose-completion-deselect-if-after. --- lisp/minibuffer.el | 43 +++++++++++++++++++++++++++++++++++++++---- lisp/simple.el | 11 ++++++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5c12d9fc914..382d4458e26 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2310,8 +2310,11 @@ display-completion-list (with-current-buffer standard-output (goto-char (point-max)) - (when completions-header-format - (insert (format completions-header-format (length completions)))) + (if completions-header-format + (insert (format completions-header-format (length completions))) + (unless completion-show-help + ;; Ensure beginning-of-buffer isn't a completion. + (insert (propertize "\n" 'face '(:height 0))))) (completion--insert-strings completions group-fun))) (run-hooks 'completion-setup-hook) @@ -2378,6 +2381,33 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completion-auto-deselect t + "If non-nil, deselect the selected completion candidate when you type. + +A non-nil value means that after typing, point in *Completions* +will be moved off any completion candidates. This means +`minibuffer-choose-completion-or-exit' will exit with the +minibuffer's current contents, instead of a completion candidate." + :type '(choice (const :tag "Candidates in *Completions* stay selected as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" t)) + :version "30.1") + +(defun completions--deselect () + "If point is in a completion candidate, move to just after the end of it. + +The candidate will still be chosen by `choose-completion' unless +`choose-completion-deselect-if-after' is non-nil." + (when (get-text-property (point) 'completion--string) + (goto-char (or (next-single-property-change (point) 'completion--string) + (point-max))))) + +(defun completions--after-change (_start _end _old-len) + "Update displayed *Completions* buffer after change in buffer contents." + (when completion-auto-deselect + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (completions--deselect))))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2400,6 +2430,7 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'after-change-functions #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2460,6 +2491,8 @@ minibuffer-completion-help (body-function . ,#'(lambda (_window) (with-current-buffer mainbuf + (when completion-auto-deselect + (add-hook 'after-change-functions #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4673,7 +4706,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completion-auto-deselect nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) @@ -4721,7 +4755,8 @@ minibuffer-choose-completion-or-exit contents." (interactive "P") (condition-case nil - (minibuffer-choose-completion no-exit no-quit) + (let ((choose-completion-deselect-if-after t)) + (minibuffer-choose-completion no-exit no-quit)) (error (minibuffer-complete-and-exit)))) (defun minibuffer-complete-history () diff --git a/lisp/simple.el b/lisp/simple.el index 02c68912dba..bf57b285fca 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10094,6 +10094,11 @@ next-line-completion (if pos (goto-char pos)))) (setq n (1+ n))))) +(defvar choose-completion-deselect-if-after nil + "If non-nil, don't choose a completion candidate if point is right after it. + +This makes `completions--deselect' effective.") + (defun choose-completion (&optional event no-exit no-quit) "Choose the completion at point. If EVENT, use EVENT's position to determine the starting position. @@ -10114,6 +10119,10 @@ choose-completion (insert-function completion-list-insert-choice-function) (completion-no-auto-exit (if no-exit t completion-no-auto-exit)) (choice + (if choose-completion-deselect-if-after + (if-let ((str (get-text-property (posn-point (event-start event)) 'completion--string))) + (substring-no-properties str) + (error "No completion here")) (save-excursion (goto-char (posn-point (event-start event))) (let (beg) @@ -10129,7 +10138,7 @@ choose-completion beg 'completion--string) beg)) (substring-no-properties - (get-text-property beg 'completion--string)))))) + (get-text-property beg 'completion--string))))))) (unless (buffer-live-p buffer) (error "Destination buffer is dead")) -- 2.42.1 ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-28 14:48 ` Spencer Baugh @ 2023-11-28 17:23 ` Juri Linkov 2023-11-29 0:20 ` Spencer Baugh 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-11-28 17:23 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel >> then point is moved before the suffix "(C-x *)", >> and after editing it is selected on RET. > > Ah, good catch. Fixed (by using 'completion--string to detect whether > point is on a completion rather 'mouse-face) Thank, now this works nicely. Maybe this deserves a NEWS entry as well? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-28 17:23 ` Juri Linkov @ 2023-11-29 0:20 ` Spencer Baugh 2023-12-03 17:21 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: Spencer Baugh @ 2023-11-29 0:20 UTC (permalink / raw) To: Juri Linkov; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 349 bytes --] Juri Linkov <juri@linkov.net> writes: >>> then point is moved before the suffix "(C-x *)", >>> and after editing it is selected on RET. >> >> Ah, good catch. Fixed (by using 'completion--string to detect whether >> point is on a completion rather 'mouse-face) > > Thank, now this works nicely. > Maybe this deserves a NEWS entry as well? Added. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Deselect-the-selected-completion-candidate-when-typi.patch --] [-- Type: text/x-patch, Size: 8062 bytes --] From e92d36a643df166406a8f4d9ca321213c6bb0704 Mon Sep 17 00:00:00 2001 From: Spencer Baugh <sbaugh@catern.com> Date: Thu, 23 Nov 2023 13:37:29 +0000 Subject: [PATCH] Deselect the selected completion candidate when typing minibuffer-choose-completion-or-exit submits the selected completion candidate, if any, ignoring the contents of the minibuffer. But a user might select a completion candidate and then want to type something else in the minibuffer and submit what they typed. Now typing will automatically deselect the selected completion candidate so that minibuffer-choose-completion-or-exit will not choose it. minibuffer-choose-completion has the same behavior as before, and is not affected by the deselection. * lisp/minibuffer.el (completion-auto-deselect, completions--deselect) (completions--after-change): Add. (minibuffer-completion-help): Add completions--after-change hook. (minibuffer-next-completion): Bind completion-auto-deselect to nil to avoid immediately deselecting the completion. (minibuffer-choose-completion-or-exit): Bind choose-completion-deselect-if-after so deselection takes effect. (display-completion-list): Guarantee a newline at the beginning of *Completions* to avoid ambiguity about candidate selection. * lisp/simple.el (choose-completion-deselect-if-after): Add. (choose-completion): Check choose-completion-deselect-if-after. * etc/NEWS: Announce. --- etc/NEWS | 9 +++++++++ lisp/minibuffer.el | 43 +++++++++++++++++++++++++++++++++++++++---- lisp/simple.el | 11 ++++++++++- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 6661ac70e1b..b785b91cb4b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -626,6 +626,15 @@ completions window. When the completions window is not visible, then all these keys have their usual meaning in the minibuffer. This option is supported for in-buffer completion as well. +*** Selected completion candidates are deselected on typing. +When a user types, point in the *Completions* window will be moved off +any completion candidates. 'minibuffer-choose-completion' ('M-RET') +will still choose a previously-selected completion candidate, but the +new command 'minibuffer-choose-completion-or-exit' (bound by +'minibuffer-visible-completions') will exit with the minibuffer +contents instead. The deselection behavior can be controlled with the +new user option 'completion-auto-deselect'. + ** Pcomplete --- diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5c12d9fc914..382d4458e26 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2310,8 +2310,11 @@ display-completion-list (with-current-buffer standard-output (goto-char (point-max)) - (when completions-header-format - (insert (format completions-header-format (length completions)))) + (if completions-header-format + (insert (format completions-header-format (length completions))) + (unless completion-show-help + ;; Ensure beginning-of-buffer isn't a completion. + (insert (propertize "\n" 'face '(:height 0))))) (completion--insert-strings completions group-fun))) (run-hooks 'completion-setup-hook) @@ -2378,6 +2381,33 @@ completions--fit-window-to-buffer (resize-temp-buffer-window win)) (fit-window-to-buffer win completions-max-height))) +(defcustom completion-auto-deselect t + "If non-nil, deselect the selected completion candidate when you type. + +A non-nil value means that after typing, point in *Completions* +will be moved off any completion candidates. This means +`minibuffer-choose-completion-or-exit' will exit with the +minibuffer's current contents, instead of a completion candidate." + :type '(choice (const :tag "Candidates in *Completions* stay selected as you type" nil) + (const :tag "Typing deselects any completion candidate in *Completions*" t)) + :version "30.1") + +(defun completions--deselect () + "If point is in a completion candidate, move to just after the end of it. + +The candidate will still be chosen by `choose-completion' unless +`choose-completion-deselect-if-after' is non-nil." + (when (get-text-property (point) 'completion--string) + (goto-char (or (next-single-property-change (point) 'completion--string) + (point-max))))) + +(defun completions--after-change (_start _end _old-len) + "Update displayed *Completions* buffer after change in buffer contents." + (when completion-auto-deselect + (when-let (window (get-buffer-window "*Completions*" 0)) + (with-selected-window window + (completions--deselect))))) + (defun minibuffer-completion-help (&optional start end) "Display a list of possible completions of the current minibuffer contents." (interactive) @@ -2400,6 +2430,7 @@ minibuffer-completion-help ;; If there are no completions, or if the current input is already ;; the sole completion, then hide (previous&stale) completions. (minibuffer-hide-completions) + (remove-hook 'after-change-functions #'completions--after-change t) (if completions (completion--message "Sole completion") (unless completion-fail-discreetly @@ -2460,6 +2491,8 @@ minibuffer-completion-help (body-function . ,#'(lambda (_window) (with-current-buffer mainbuf + (when completion-auto-deselect + (add-hook 'after-change-functions #'completions--after-change t)) ;; Remove the base-size tail because `sort' requires a properly ;; nil-terminated list. (when last (setcdr last nil)) @@ -4673,7 +4706,8 @@ minibuffer-next-completion (next-line-completion (or n 1)) (next-completion (or n 1))) (when auto-choose - (let ((completion-use-base-affixes t)) + (let ((completion-use-base-affixes t) + (completion-auto-deselect nil)) (choose-completion nil t t)))))) (defun minibuffer-previous-completion (&optional n) @@ -4721,7 +4755,8 @@ minibuffer-choose-completion-or-exit contents." (interactive "P") (condition-case nil - (minibuffer-choose-completion no-exit no-quit) + (let ((choose-completion-deselect-if-after t)) + (minibuffer-choose-completion no-exit no-quit)) (error (minibuffer-complete-and-exit)))) (defun minibuffer-complete-history () diff --git a/lisp/simple.el b/lisp/simple.el index 35bce6ab4b8..67d04032e64 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10092,6 +10092,11 @@ next-line-completion (if pos (goto-char pos)))) (setq n (1+ n))))) +(defvar choose-completion-deselect-if-after nil + "If non-nil, don't choose a completion candidate if point is right after it. + +This makes `completions--deselect' effective.") + (defun choose-completion (&optional event no-exit no-quit) "Choose the completion at point. If EVENT, use EVENT's position to determine the starting position. @@ -10112,6 +10117,10 @@ choose-completion (insert-function completion-list-insert-choice-function) (completion-no-auto-exit (if no-exit t completion-no-auto-exit)) (choice + (if choose-completion-deselect-if-after + (if-let ((str (get-text-property (posn-point (event-start event)) 'completion--string))) + (substring-no-properties str) + (error "No completion here")) (save-excursion (goto-char (posn-point (event-start event))) (let (beg) @@ -10127,7 +10136,7 @@ choose-completion beg 'completion--string) beg)) (substring-no-properties - (get-text-property beg 'completion--string)))))) + (get-text-property beg 'completion--string))))))) (unless (buffer-live-p buffer) (error "Destination buffer is dead")) -- 2.42.1 ^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-11-29 0:20 ` Spencer Baugh @ 2023-12-03 17:21 ` Juri Linkov 2023-12-03 18:13 ` Eli Zaretskii 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-12-03 17:21 UTC (permalink / raw) To: Spencer Baugh; +Cc: emacs-devel >>>> then point is moved before the suffix "(C-x *)", >>>> and after editing it is selected on RET. >>> >>> Ah, good catch. Fixed (by using 'completion--string to detect whether >>> point is on a completion rather 'mouse-face) >> >> Thank, now this works nicely. >> Maybe this deserves a NEWS entry as well? > > Added. Thanks, with no more comments in three days, this is pushed now. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-12-03 17:21 ` Juri Linkov @ 2023-12-03 18:13 ` Eli Zaretskii 2023-12-06 17:20 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: Eli Zaretskii @ 2023-12-03 18:13 UTC (permalink / raw) To: Juri Linkov; +Cc: sbaugh, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: emacs-devel@gnu.org > Date: Sun, 03 Dec 2023 19:21:29 +0200 > > >>>> then point is moved before the suffix "(C-x *)", > >>>> and after editing it is selected on RET. > >>> > >>> Ah, good catch. Fixed (by using 'completion--string to detect whether > >>> point is on a completion rather 'mouse-face) > >> > >> Thank, now this works nicely. > >> Maybe this deserves a NEWS entry as well? > > > > Added. > > Thanks, with no more comments in three days, this is pushed now. I don't understand the NEWS entry: *** Selected completion candidates are deselected on typing. When a user types, point in the *Completions* window will be moved off any completion candidates. 'minibuffer-choose-completion' ('M-RET') will still choose a previously-selected completion candidate, but the new command 'minibuffer-choose-completion-or-exit' (bound by 'minibuffer-visible-completions') will exit with the minibuffer contents instead. The deselection behavior can be controlled with the new user option 'completion-auto-deselect'. What are "selected completion candidates"? I typed "C-x C-f TAB", and Emacs popped the completions buffer, but without "selecting" any of the candidates. Searching for "select" in the buffer produced by "M-x apropos complet RET" doesn't seem to find anything pertinent. And there's nothing about this in the manual. What am I missing? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-12-03 18:13 ` Eli Zaretskii @ 2023-12-06 17:20 ` Juri Linkov 2023-12-06 17:50 ` Eli Zaretskii 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-12-06 17:20 UTC (permalink / raw) To: Eli Zaretskii; +Cc: sbaugh, emacs-devel > I don't understand the NEWS entry: > > *** Selected completion candidates are deselected on typing. > When a user types, point in the *Completions* window will be moved off > any completion candidates. 'minibuffer-choose-completion' ('M-RET') > will still choose a previously-selected completion candidate, but the > new command 'minibuffer-choose-completion-or-exit' (bound by > 'minibuffer-visible-completions') will exit with the minibuffer > contents instead. The deselection behavior can be controlled with the > new user option 'completion-auto-deselect'. > > What are "selected completion candidates"? I typed "C-x C-f TAB", and > Emacs popped the completions buffer, but without "selecting" any of > the candidates. Searching for "select" in the buffer produced by > "M-x apropos complet RET" doesn't seem to find anything pertinent. > And there's nothing about this in the manual. What am I missing? Maybe a better term would be "a highlighted completion candidate"? This should denote a candidate where point is located in the *Completions* buffer. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-12-06 17:20 ` Juri Linkov @ 2023-12-06 17:50 ` Eli Zaretskii 2023-12-07 7:44 ` Juri Linkov 0 siblings, 1 reply; 62+ messages in thread From: Eli Zaretskii @ 2023-12-06 17:50 UTC (permalink / raw) To: Juri Linkov; +Cc: sbaugh, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: sbaugh@catern.com, emacs-devel@gnu.org > Date: Wed, 06 Dec 2023 19:20:40 +0200 > > > I don't understand the NEWS entry: > > > > *** Selected completion candidates are deselected on typing. > > When a user types, point in the *Completions* window will be moved off > > any completion candidates. 'minibuffer-choose-completion' ('M-RET') > > will still choose a previously-selected completion candidate, but the > > new command 'minibuffer-choose-completion-or-exit' (bound by > > 'minibuffer-visible-completions') will exit with the minibuffer > > contents instead. The deselection behavior can be controlled with the > > new user option 'completion-auto-deselect'. > > > > What are "selected completion candidates"? I typed "C-x C-f TAB", and > > Emacs popped the completions buffer, but without "selecting" any of > > the candidates. Searching for "select" in the buffer produced by > > "M-x apropos complet RET" doesn't seem to find anything pertinent. > > And there's nothing about this in the manual. What am I missing? > > Maybe a better term would be "a highlighted completion candidate"? > This should denote a candidate where point is located in the > *Completions* buffer. Thanks, but I still don't think I follow. How do I get this situation where a completion candidate is "highlighted" or "selected", starting from typing a command like "C-x C-f" and then typing TAB? ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-12-06 17:50 ` Eli Zaretskii @ 2023-12-07 7:44 ` Juri Linkov 2023-12-08 8:46 ` Eli Zaretskii 0 siblings, 1 reply; 62+ messages in thread From: Juri Linkov @ 2023-12-07 7:44 UTC (permalink / raw) To: Eli Zaretskii; +Cc: sbaugh, emacs-devel >> > *** Selected completion candidates are deselected on typing. >> > When a user types, point in the *Completions* window will be moved off >> > any completion candidates. 'minibuffer-choose-completion' ('M-RET') >> > will still choose a previously-selected completion candidate, but the >> > new command 'minibuffer-choose-completion-or-exit' (bound by >> > 'minibuffer-visible-completions') will exit with the minibuffer >> > contents instead. The deselection behavior can be controlled with the >> > new user option 'completion-auto-deselect'. >> > >> > What are "selected completion candidates"? I typed "C-x C-f TAB", and >> > Emacs popped the completions buffer, but without "selecting" any of >> > the candidates. Searching for "select" in the buffer produced by >> > "M-x apropos complet RET" doesn't seem to find anything pertinent. >> > And there's nothing about this in the manual. What am I missing? >> >> Maybe a better term would be "a highlighted completion candidate"? >> This should denote a candidate where point is located in the >> *Completions* buffer. > > Thanks, but I still don't think I follow. How do I get this situation > where a completion candidate is "highlighted" or "selected", starting > from typing a command like "C-x C-f" and then typing TAB? C-x C-f TAB TAB M-<down> Then e.g. DEL (<backspace>) will "deselect" the highlighted candidate. ^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: Navigating completions from minibuffer 2023-12-07 7:44 ` Juri Linkov @ 2023-12-08 8:46 ` Eli Zaretskii 0 siblings, 0 replies; 62+ messages in thread From: Eli Zaretskii @ 2023-12-08 8:46 UTC (permalink / raw) To: Juri Linkov; +Cc: sbaugh, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: sbaugh@catern.com, emacs-devel@gnu.org > Date: Thu, 07 Dec 2023 09:44:45 +0200 > > >> > *** Selected completion candidates are deselected on typing. > >> > When a user types, point in the *Completions* window will be moved off > >> > any completion candidates. 'minibuffer-choose-completion' ('M-RET') > >> > will still choose a previously-selected completion candidate, but the > >> > new command 'minibuffer-choose-completion-or-exit' (bound by > >> > 'minibuffer-visible-completions') will exit with the minibuffer > >> > contents instead. The deselection behavior can be controlled with the > >> > new user option 'completion-auto-deselect'. > >> > > >> > What are "selected completion candidates"? I typed "C-x C-f TAB", and > >> > Emacs popped the completions buffer, but without "selecting" any of > >> > the candidates. Searching for "select" in the buffer produced by > >> > "M-x apropos complet RET" doesn't seem to find anything pertinent. > >> > And there's nothing about this in the manual. What am I missing? > >> > >> Maybe a better term would be "a highlighted completion candidate"? > >> This should denote a candidate where point is located in the > >> *Completions* buffer. > > > > Thanks, but I still don't think I follow. How do I get this situation > > where a completion candidate is "highlighted" or "selected", starting > > from typing a command like "C-x C-f" and then typing TAB? > > C-x C-f TAB TAB M-<down> > > Then e.g. DEL (<backspace>) will "deselect" the highlighted candidate. Thanks, I tried to improve the documentation using the above information. Btw, I notice (with some sadness) that we are largely back to our previous practice of not updating the manuals with information about changes. This has two adverse consequences: . the release cycle of the next major release will take longer . the manuals included in the next major release are more likely to be inaccurate and/or incomplete People who complain about too slow release schedule of Emacs should take note, and hopefully invest more effort in the future in updating the documentation together with code changes. ^ permalink raw reply [flat|nested] 62+ messages in thread
end of thread, other threads:[~2023-12-08 8:46 UTC | newest] Thread overview: 62+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-11-07 3:57 Navigating completions from minibuffer T.V Raman 2023-11-07 4:55 ` T.V Raman 2023-11-07 7:20 ` Juri Linkov 2023-11-07 17:53 ` T.V Raman 2023-11-07 19:36 ` T.V Raman 2023-11-08 7:39 ` Juri Linkov 2023-11-08 16:21 ` T.V Raman 2023-11-08 17:18 ` T.V Raman 2023-11-08 22:11 ` Slow completion-at-point was " T.V Raman 2023-11-09 7:22 ` Slow completion-at-point Juri Linkov 2023-11-09 12:10 ` Dmitry Gutov 2023-11-09 16:20 ` Juri Linkov 2023-11-09 18:32 ` João Távora 2023-11-11 2:48 ` T.V Raman 2023-11-11 10:36 ` Dmitry Gutov 2023-11-11 16:40 ` T.V Raman 2023-11-11 19:00 ` Juri Linkov 2023-11-11 19:43 ` T.V Raman 2023-11-11 21:50 ` Dmitry Gutov 2023-11-09 15:39 ` T.V Raman 2023-11-09 12:20 ` Slow completion-at-point was Re: Navigating completions from minibuffer Dmitry Gutov 2023-11-09 15:41 ` T.V Raman 2023-11-09 17:46 ` T.V Raman 2023-11-10 13:12 ` Spencer Baugh 2023-11-11 18:58 ` Juri Linkov 2023-11-14 7:36 ` Juri Linkov 2023-11-15 21:40 ` Spencer Baugh 2023-11-16 17:15 ` T.V Raman 2023-11-15 22:03 ` Spencer Baugh 2023-11-16 7:16 ` Juri Linkov 2023-11-16 14:41 ` Spencer Baugh 2023-11-16 17:28 ` Juri Linkov 2023-11-16 18:25 ` Spencer Baugh 2023-11-17 7:09 ` Juri Linkov 2023-11-17 17:22 ` Spencer Baugh 2023-11-18 20:58 ` sbaugh 2023-11-19 7:08 ` Juri Linkov 2023-11-19 8:19 ` Eli Zaretskii 2023-11-19 14:41 ` Spencer Baugh 2023-11-19 18:01 ` Juri Linkov 2023-11-19 19:41 ` Spencer Baugh 2023-11-20 2:58 ` Spencer Baugh 2023-11-23 13:39 ` sbaugh 2023-11-24 7:54 ` Juri Linkov 2023-11-25 15:19 ` Spencer Baugh 2023-11-25 16:08 ` Eli Zaretskii 2023-11-25 18:23 ` sbaugh 2023-11-25 18:48 ` Juri Linkov 2023-11-26 13:10 ` sbaugh 2023-11-25 19:00 ` Eli Zaretskii 2023-11-25 17:46 ` Juri Linkov 2023-11-26 14:33 ` Spencer Baugh 2023-11-27 7:22 ` Juri Linkov 2023-11-28 14:48 ` Spencer Baugh 2023-11-28 17:23 ` Juri Linkov 2023-11-29 0:20 ` Spencer Baugh 2023-12-03 17:21 ` Juri Linkov 2023-12-03 18:13 ` Eli Zaretskii 2023-12-06 17:20 ` Juri Linkov 2023-12-06 17:50 ` Eli Zaretskii 2023-12-07 7:44 ` Juri Linkov 2023-12-08 8:46 ` Eli Zaretskii
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.