-
-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic candidates - Is it possible to implement consult-buffer more generically? #10
Comments
The dynamic candidate list issue also applies to:
Any ideas would be welcome here, regarding on how to implement dynamic candidate lists. There has been some discussion on the selectrum tracker: |
There is the async-completing-read package by oantolin, see the comment #29 (comment) |
This minimal example from the selectrum tracker works for me in "emacs -Q".
(icomplete-mode)
(let ((colla '("this" "is" "the" "colla"))
(collb '("now" "showing" "collb")))
(defun test (str pred action)
(if (eq action 'metadata)
`(metadata
(cycle-sort-function . identity)
(display-sort-function . identity))
(let ((coll (cond ((string-prefix-p "a " str)
colla)
((string-prefix-p "b " str)
collb)
(t
(append colla collb)))))
(cond ((null action)
(cond ((or (string-prefix-p "a " str)
(string-prefix-p "b " str))
(concat (substring str 0 2)
(try-completion (substring str 2) coll pred)))
(t (try-completion str coll pred))))
((eq action t)
(all-completions
(cond ((or (string-prefix-p "a " str)
(string-prefix-p "b " str))
(substring str 2))
(t str))
coll pred))
(t nil))))))
(define-key minibuffer-local-completion-map (kbd "SPC") 'self-insert-command)
(completing-read "Check: " 'test) |
I just want to point for people reading this that haven't followed the link to the orderless issue, that I think I've shown there that it isn't orderless's fault this example doesn't work, as it doesn't work with the substring or partial-completion styles either. |
@oantolin Sorry, I should have made this more clear. This is example is something where we tried out on how to implement this - I am kind of clueless here how to do the dynamic candidate completion correctly. |
Why not just add some Now P.S. This is more about |
Yes, this would be possible but it introduces an intermediate step. The way it is now allows faster switching. And you can always bind consult-recent-file to another key, e.g. C-x C-r. |
I know. But if I restart emacs and continue working on my project I've tried to switch to that project's buffer. There is no buffer from that project. I should press Now I use |
I think you might be interested in |
Will try it, thank you. |
@oantolin unfortunately it doesn't work for me. I can see:
|
Sorry, I should have explained how to use it. First of all, you don't need to setup any extra bindings. I think you mentioned trying to switch to some buffer, and realizing you don't have it open and needed to do For even more convenience you can setup a keymap containing short bindings for all the commads you think you might want to jump between. For example, embark comes with an You could replace the keybindings in Hope that's understandable. Please ask me anything, if you have further questions. |
(I know embark's documentation is pretty sparse, I will work on it soon.) |
Thank you for explanation. I will try to realize it tomorrow :) |
Hey, @minad, how about something vanilla like this for (defvar buffer-candidates '("asasdd" "elkfjq" "slfjdsk"))
(defvar file-candidates '("lerjkdd" "lskfjdq" "iuyewn"))
(defvar all-candidates (append buffer-candidates file-candidates))
(defvar current-candidates all-candidates)
;; the following 3 commands should also refresh the completion UI
(defun narrow-to-buffers ()
(interactive)
(setq current-candidates buffer-candidates))
(defun narrow-to-files ()
(interactive)
(setq current-candidates file-candidates))
(defun widen-to-all ()
(interactive)
(setq current-candidates all-candidates))
(defvar narrow-candidates-map
(let ((map (make-sparse-keymap "Candidates: ")))
(define-key map (kbd "a") '("all" . widen-to-all))
(define-key map (kbd "f") '("files" . narrow-to-files))
(define-key map (kbd "b") '("buffers" . narrow-to-buffers))
map))
(define-key minibuffer-local-completion-map (kbd "^") #'narrow-candidates-map)
(defun mock-switcher ()
(interactive)
(completing-read "Switch to: "
;; the following looks a little like it should be
;; equivalent to passing just current-candidates,
;; but the lambda delays evaluation of
;; current-candidates until completion time
(lambda (string predicate action)
(complete-with-action action current-candidates
string predicate)))) An alternative would be to make the |
We can figure out a way to make the key binding to the |
To be concrete, for the command version I mean: (defvar buffer-candidates '("asasdd" "elkfjq" "slfjdsk"))
(defvar file-candidates '("lerjkdd" "lskfjdq" "iuyewn"))
(defvar all-candidates (append buffer-candidates file-candidates))
(defvar current-candidates all-candidates)
;; the following command should also refresh the completion UI
(defun choose-candidates (ch)
(interactive "cCandidates: [a]ll, [b]uffers, [f]iles")
(setq current-candidates
(pcase ch
(?a all-candidates)
(?b buffer-candidates)
(?f file-candidates))))
(define-key minibuffer-local-completion-map (kbd "^") #'choose-candidates)
(defun mock-switcher ()
(interactive)
(completing-read "Switch to: "
;; the following looks a little like it should be
;; equivalent to passing just current-candidates,
;; but the lambda delays evaluation of
;; current-candidates until completion time
(lambda (string predicate action)
(complete-with-action action current-candidates
string predicate)))) |
And as a little nicety, one could update the prompt to indicate what candidates are being considered. |
@oantolin These are nice ideas, with the ^ prefix. I will try it! |
@oantolin @clemera How do you change the prompt? By messing with the minibuffer content? But regarding this I guess I have to wait a bit until Selectrum gets support for dynamic candidates via the completing-read API, or I have to do something about it. But given my non-familiarity with the Selectrum code... When looking at this example, I get the feeling that the dynamic aspect of the completing-read API is badly designed. For example if you pass a lambda as completion table and only overwrite sorting, but the candidates are still static, you force expensive reevaluation. Right now selectrum makes the choice to never reevaluate which is fast, but wrong. I think there should be some way to tell the completion-system that candidate reevaluation is not desired. Something like a metadata property 'candidates-are-actually-static. As far as I understood the current plan in Selectrum was to hard code a list of static completion tables (or maybe make this configurable). Alternatively one could offer a Selectrum option which allows to configure the behavior. I could configure that setting in consult as I already do for other things: Lines 327 to 329 in b02c191
Regarding icomplete, I am not sure what can be done here. Maybe propose something for upstream |
Could you elaborate?
I think this because dynamic completion was probably added as an afterthought. The default completion mechanism only computes completions when you press TAB and there where no "continued completion" frameworks back then. Generally bringing the fruits of the discussion around this up to Emacs devel would be great! @oantolin With Selectrum you can make the example currently work like this: (define-key selectrum-minibuffer-map (kbd "^") #'choose-candidates)
(defun choose-candidates (ch)
(interactive "cCandidates: [a]ll, [b]uffers, [f]iles")
(setq current-candidates
(pcase ch
(?a all-candidates)
(?b buffer-candidates)
(?f file-candidates)))
(setq-local selectrum--previous-input-string nil)
(setq-local selectrum--preprocessed-candidates
current-candidates)) |
@oantolin proposed to change the prompt during an active completing-read session. I don't know how to do it. Therefore my question: How do you change the prompt?
Yes, we should do that. I also mentioned something like that in radian-software/selectrum#225. By improving the standard API we can improve the situation for everyone and we (or rather you as selectrum developer) are in the best position to make well-informed proposals to upstream. This should be somehow backwards compatible, ideally with graceful degradation. If we can make things work in icomplete and in selectrum with additional selectrum-specific non-recompute optimizations it would be great. These selectrum-specifics could then be integrated into the official API at some point. |
Thanks, I missed that. You could use an overlay for that, see for example |
Maybe we could allow recomputation of small tables by default (less then 1000 cands returned initially or something similar, could be configureable), that would probably work out for most cases and if not you can set it to most positive fix num to force recomputation even for bigger ones. |
@minad said:
You can do whatever you want in a lambda! If you don't want to recompute, cache. There is even a built-in
I agree that Selectrum is wrong about this, but don't judge. Orderless is wrong too! It only calls the completion table on the prefix (usually empty or, for file completion, the directory) and does all filtering via @clemera said:
Yes, I was too lazy to include the code so I just added a comment to the example saying it should be done: @minad asked how to change the prompt, and @clemera said:
I forgot at the time I wrote "we can figure out" what the trick was, but yes an overlay is the way to go. The builtin modes minibuffer-electric-default-mode, minibuffer-depth-indicate-mode and minibuffer-eldef-shorten-default, use overlays, as does Embark to add an indicator to the minibuffer prompt, for example. |
Technically yes, but UI experience is more important to me in this case. Though I would be really happy if there would be better compromise than what we have currently. |
I agree, and I did things like this. But there is just one thing which confuses me - I perceive icomplete as slower than selectrum, it also seems to create more load/computations. And I don't get why. In particular if you use M-x which is a large table. As if it is always lagging a bit behind my keypresses. I set the delay to 0.
What does that mean? So if the candidates change at some point, orderless won't see the new candidates? |
No, no, don't worry. I'm not quite as reckless as the selectrum people 😛 Orderless does call the completion table (with the prefix as argument) on every keystroke, so it does pick up changes (I actually only tested the above code that changes But orderless is still wrong since a completion table can do whatever the hell it wants. It can decide that the empty string matches My sincere hope is that nobody will write a completion table orderless gives wrong answers on. |
No risk now fun 😎 There are not too many dynamic tables which need recomputation on every key press so usually we get away with it. But as I said I would also like to support all cases, just in a way they don't destroy the general UI experience we currently have. |
@oantolin What I wonder about when the table uses |
|
Ah yes, but that means if you have a sequence you are better off not passing it to |
Yes, I think that's correct. |
Okay thanks! |
I noticed many tables use |
I have a working solution in #55. |
Right now there are two implementations:
The text was updated successfully, but these errors were encountered: