Closed clemera closed 3 years ago
completion-styles
andcompletion-styles-alist
There's also completion-category-overrides
.
Oh yes, overlooked that one.
Is the behavior generated by the default completion-styles
equivalent to what we currently have as the default filtering behavior?
No but some default completion styles also don't make complete sense for selectrum because they were not designed for the interactive narrowing selectrum does(maybe we would need to exclude those). The available completion styles are described in the description strings of the elements in completion-styles-alist
).
For completion-styles
support with selectrum-refine-candidates-function
it would probably need to accept a regular completion table as input (but this would be a breaking change for people who use filter functions which only accepts list of strings). Or we could decide to only make use of completion styles when selectrum-refine-candidates-function
is set to nil (which could be the default for selectrum, most people probably use prescient
or other string filter functions anyway).
If the default behavior of completion-styles
is not as good as what we have by default in Selectrum, then what is the benefit from adding support for it, as opposed to just making the default behavior of Selectrum dependent on some of the completion user options?
One benefit would be that the user sets completion-styles
according to his preferences in his configuration and in the ideal case it's automatically picked up by frameworks. Helm has recently added support for it as well and there is an open issue for ivy. The idea is that when users configure completion-styles
they get the same filtering behaviour everywhere.
Another one is that via completion-category-defaults
and completion-category-overrides
users can configure custom filtering for different categories. The category can be configured by the metadata of a completion table.
It's true that the current built-in completion-styles
are not exhaustive in current Emacs versions (in Emacs 27 flex
was added at least) but other packages can provide them, too (there already exits orderless
for example which we talked about earlier).
But I agree that support of completion-styles
are not essential and I'm not sure custom filtering per category as described above makes much sense for selectrums model where you likely want the same filtering behaviour everywhere. In addition to that I believe one downside of using completion styles is that we would have to do filtering and highlighting in one step opposed to the more efficient way selectrum currently does it (only highlight the displayed candidates).
I think more important (but also easier) would be adding support for display-sort-function
metadata of completing read collections which are functions. Currently selectrum ignores any metadata of those collections and uses (funcall collection "" predicate t)
to retrieve the candidates. Letting completion tables enforce a specific sorting makes sense because there is no global variable for default completion to control sorting. The default completion behaviour also sorts the candidates but with this metadata completion tables have control over it (see tmm--completion-table
for a built-in example which uses the metadata to disable sorting).
That seems reasonable.
This seems like exactly what I'm missing from Selectrum currently. I'm trying to migrate from icomplete
with partial-completion
style, which allows me to type C-xC-f~/D/r-g/s/
TAB and get ~/Dev/renpy-games/scratchpad/
back. I prefer Selectrum's UI to icomplete
's, and I'm scared away from Ivy by its tangled mess of interdependencies with Consul and Swiper, but the fact I can't get the same completion style with either plain Selectrum or Selectrum + prescient is a huge regression for my workflow.
This seems like exactly what I'm missing from Selectrum currently. I'm trying to migrate from
icomplete
withpartial-completion
style, which allows me to type C-xC-f~/D/r-g/s/
TAB and get~/Dev/renpy-games/scratchpad/
back. I prefer Selectrum's UI toicomplete
's, and I'm scared away from Ivy by its tangled mess of interdependencies with Consul and Swiper, but the fact I can't get the same completion style with either plain Selectrum or Selectrum + prescient is a huge regression for my workflow.
@mathrick, in the mean time, have you tried the package icomplete-vertical
? Maybe its UI is close enough for now.
@okamsn: that is working great so far, thanks for the suggestion!
Stefan Monnier sent me the following patch by personal email:
diff --git a/selectrum.el b/selectrum.el
index 6d0956f..59dfb14 100644
--- a/selectrum.el
+++ b/selectrum.el
@@ -772,6 +763,9 @@ PRED defaults to `minibuffer-completion-predicate'."
selectrum--refined-candidates)))
-1)
((and selectrum--init-p
+ ;; FIXME: Check the `category' returned by
+ ;; `completion-metadata' instead of
+ ;; `minibuffer-completing-file-name'!
minibuffer-completing-file-name
(eq minibuffer-completion-predicate
'file-directory-p)
I think this is the issue thread that tracks support for completion-metadata
features, right? If so, here's another one.
@clemera @raxod502 Would it make sense to revisit this issue - is it still necessary to have separate refinement and highlighting functions instead of using the standard completion style facilities? In particular if the idea is to move closer to the standard API with regards to configuration.
EDIT: The following seems to work with orderless for example. But the selectrum API is clearly better since in that case the highlights are only applied to the visible candidates and not to all filtered candidates.
(setq selectrum-refine-candidates-function
(lambda (string candidates)
(let ((result (completion-all-completions string candidates nil (length string))))
(let ((last result))
(while last
(unless (listp (cdr last))
(setcdr last nil))
(setq last (cdr last))))
result)))
(setq selectrum-highlight-candidates-function (lambda (_ candidates) candidates))
EDIT2: @oantolin told be that the function can be improved.
(setq selectrum-refine-candidates-function
(lambda (string candidates)
(let* ((result (completion-all-completions string candidates nil (length string)))
(last (last result)))
(setcdr last nil)
result)))
I think it's a fine idea to use the default facilities, as long as it does not make it a lot more difficult for a new user to configure the things that are currently supported by the refinement and highlighting functions, which seem fairly straightforward in usage to me. That could be either by making it clear how to make simple adjustments using the completing-read
-style API, or by adding a wrapper function that can be used for these kinds of adjustments.
One possibility would be to keep the current selectrum-refine-candidates-function
and selectrum-highlight-candidates-function
variables and simply set their default value to the values @minad suggested. That way someone who uses Selectrum's default values gets the benefit of Emacs' completion-styles. The out of the box experience with Selectrum is pretty limited, since it only matches substrings, using the default value of completion-styles
is already a big improvement.
That sounds perfect to me.
using the default value of completion-styles is already a big improvement.
And when the user has already configured completion styles there is no additional setup needed which is also nice, it is less optimal as all candidates get highlighted but overall it is really nice we can support them now. There is this first next char highlighting which makes more sense with default completion but it doesn't harm and we also have it with completion-in-region where we use completion-all-completions
.
What about selectrum-primary-highlight
and selectrum-secondary-highlight
faces? I think it makes sense to remove them? Probably better to let the configured filtering/highlighting packages take care of this? @raxod502 could you move those to selectrum-prescient
which uses these?
You can also remove the current default functions then? But it should still be possible to use specialized functions for refine/highlight, since having those separate is arguably better because of the highlighting which doesn't have to be applied to every candidate.
Completion styles are now used by default see #330
Do you think many people used the old default? I think the default completion-styles does not include substring
, so people who used the old Selectrum default and never configured any completion-styles will be a little surprised that they can't type stuff from the middle of the candidates anymore.
(Sorry I didn't remember the default value of completion-styles when we were discussing this a while ago.)
@oantolin I think it is okay to break this now, better now than later. And we will see if bug reports are incoming. I think most Selectrum users have configured Prescient, since this is most prominently announced in the readme. Let's not keep legacy code around!
EDIT: But maybe document the fix to add substring to completion-styles? Is this sufficient?
I expect no one to use the default, the README also mentions the the default is very basic so having bad filtering out of the box is expected and what we had before wasn't that much better.
Mentioning substring in the README is a good idea, maybe also the new flex style
Yeah, with only built-in styles, I'd say (setq completion-styles '(substring partial-completion))
is already much more usable than the old default. And I don't like flex
, but lots of people love it.
Let's not keep legacy code around!
I wasn't suggesting that, @minad.
And speaking of legacy code, should I remove orderless-filter and orderless-highlight? I guess Selectrum users might still want to avoid highlighting all matches, so maybe it is still worth it to have those around.
And speaking of legacy code, should I remove orderless-filter and orderless-highlight? I guess Selectrum users might still want to avoid highlighting all matches, so maybe it is still worth it to have those around.
Please keep them, it is useful to be able doing only one of those, for some purposes you don't want highlighting and as you mentioned it is also more efficient.
Sorry, @oantolin. I didn't want to imply that you did. Just stating my opinion.
And speaking of legacy code, should I remove orderless-filter and orderless-highlight? I guess Selectrum users might still want to avoid highlighting all matches, so maybe it is still worth it to have those around.
I don't consider these legacy and I would prefer if we could keep them. They have the advantage that you don't have to apply highlighting to all candidates. I don't know how much it costs in practice. I also have special support for refine/highlight in Consult, see https://github.com/minad/consult/commit/61200524a27f14f9a50cbe027c855ff99c42bf75.
@oantolin @minad I have added your recommendation to the README. What remains to make Selectrum fully API compatible? I know of completion boundaries, anything else you can think of?
OK, I'll keep them, keeping is also less work. 😉
And the problems with fully dynamic tables...
And the problems with fully dynamic tables...
Instead of adding support it may make sense to ask upstream regarding some completion-exhibit/refresh
function which can be set by completion-systems. This is better than doing some kind of automatic refresh or polling. This strategy works well in Consult. But I don't know if there are other use cases for dynamic completion tables which would work better with the strategy to re-read the table every time.
Regarding re-reading, I wonder where the slowdown actually comes from - maybe only M-x obarray is a problem? From my what I see, all the static consult functions will simply return the static list and this is not significantly more costly than accessing a cached variable inside of selectrum. So as an alternative, what about switching to re-reading every time and only exclude the problematic ones via some exclusion list?
Instead of adding support it may make sense to ask upstream regarding some completion-exhibit/refresh function which can be set by completion-systems. This is better than doing some kind of automatic refresh or polling.
That would be great, the default value could maybe be minibuffer-completion-help
then.
So as an alternative, what about switching to re-reading every time and only exclude the problematic ones via some exclusion list?
That is also something we considered in the past, but there would still be a problem I noticed: When calling the table with the string as input (currently we pass the empty string) and the table uses complete-with-action
the candidates are pre-filtered by matching prefix while for Selectrum we would want to get all the candidates and do the filtering ourselves. On the other hand not passing the input breaks completion tables which where the returned candidate set depends on the input.
That is also something we considered in the past, but there would still be a problem I noticed: When calling the table with the string as input (currently we pass the empty string) and the table uses complete-with-action the candidates are pre-filtered by matching prefix while for Selectrum we would want to get all the candidates and do the filtering ourselves. On the other hand not passing the input breaks completion tables which where the returned candidate set depends on the input.
I see! Given that I would probably do nothing and add no special support for dynamic tables. It seems the only correct way to resolve this then is to do it 100% in the standard way and give up your own filtering. But this is something you probably don't want to do given that there are certain advantages of your current approach?
I have considered rebinding all-completions
using cl-letf
to circumvent this. When the passed string would equal the minibuffer input we could pass the empty string. I'm not sure I would want to go with this though for now things are working well, I will wait for additional complaints about tables not working before looking further into that ;)
Makes sense to wait.
Out of interest, what prevents you from going 100% the standard way? Would it slow things too much, since highlighting would be applied to everything? Is this the only thing or are there more things where you would lose too control? Regarding the unnecessary highlighting one could also use a different approach - set some buffer-local flag which should then be read by the completion-style. Depending on the flag highlighting is either applied or not.
I'm not sure what you mean, with support for completion-styles
we are already doing filtering/highlighting the standard way what do you think is missing?
I think @minad meant removing the refine-candidates and highlight functions and only using completion styles (not just using them as default, but always).
Yes, thank you for clarifying, @oantolin.
I see, makes sense. I like the current way better, even when completion-styles would check for such a flag, as it is more flexible, the client can use any filtering/highlighting wanted and they are not coupled.
@clemera That's fine. I was just wondering if there is a deeper technical reason which makes your approach even necessary. Given that you are slightly deviating from the "standard way" here I think it is acceptable to not pursue support for dynamic tables. At least until we see a compelling reason to add support. As we agreed above - waiting is the best option.
A problem I noticed with this is that when the user uses non default functions (for example by using prescient) locally set completions styles are not picked up automatically (locally changed completion styles are probably not common but it seems you want to use this in consult?) If prescient gains support for it it might be better to remove the distinction in the end.
locally changed completion styles are probably not common but it seems you want to use this in consult?
Yes, I use this for the async functions which install this splitting completion style. But I added special support for Selectrum too.
EDIT: See here https://github.com/minad/consult/blob/d11cdb239008903158bf4531c19be5d568d6a86d/consult-selectrum.el#L61
@mathrick
This seems like exactly what I'm missing from Selectrum currently. I'm trying to migrate from icomplete with partial-completion style, which allows me to type C-xC-f~/D/r-g/s/TAB and get ~/Dev/renpy-games/scratchpad/ back
With #390 you can now do that, when you type the pattern you automatically get offered the possible candidates of partial matches.
@clemera: Awesome, I'll give it a spin!
@clemera: This is looking very nice!
I found one bug and one case in which selectrum's behaviour is less useful than icomplete
's. This assumes @minad's snippet to use default completion-styles
. I'm using the Emacs 26 default value for it, ie. '(basic partial-completion emacs22)
/
. That is, if I type ~/D/fo/ba.t
, I won't get ~/Dev/foo/bar.txt
, in fact no candidates at all will be available. I need to back up to ~/D/fo/
(note the trailing slash, ~/D/fo
will again result in no candidates), at which point ~/Dev/foo/bar.txt
becomes the only candidate. I need to delete the slash, enter it again, press TAB to get ~/Dev/foo/
expanded, then I can see bar.txt
as well as any other file in ~/Dev/foo/
icomplete
, /ss<TAB>
gives me /ssh:
and /sshx:
as candidates, and /ssh:mat<TAB>
results in /ssh:mathrick-thinkpad:
(which is a host I've previously connected to, I'm actually not sure how and where that is stored). None of these work with selectrum-read-file-name
.@minad: There's a small bug in your completion-styles
adapter. Should be:
(setq! selectrum-refine-candidates-function
(lambda (string candidates)
(let ((result (completion-all-completions string candidates nil (length string))))
(when result
(setcdr (last result) nil))
result)))
Without (when result ...)
, it will cause an error if no results were returned.
@mathrick Thanks for your feedback!
paths aren't actually expanded until the last character is /.
The idea is that once you typed a /
you get all the partial completions for that path level and then can filter them down using configured filter function/ completion-styles. Except that for the filtering itself partial-completion for file names still does not work because of reasons how we handle the whole thing internally. But you could add the flex
style which exists in Emacs 27 which would filter the results in a similar way for this use case. I will think about ways we could work around this automatically so you wouldn't notice the difference UI wise when are used to getting matches in you example when using partial-completion style for filtering.
TRAMP methods and hosts cannot be completed
That is a good observation, I don't know where these completions are coming from with icomplete, I will open an issue for it.
Without (when result ...), it will cause an error if no results were returned.
Did you experienced one? Because (nconc nil nil)
works fine.
@clemera I think @mathrick looked at the outdated version proposed above. The actual version we are using is without issues.
@minad
EDIT: I realized you probably referred only to the completion-styles adapter comment. The other points seem to apply, when trying to use partial completion on the results coming from partial input pattern it won't work. For example /u/s/e/s/d.el
should have /usr/share/emacs/site-lisp/debian-startup.el
in the results (when that path exists). Using (setq completion-styles '(partial-completion orderless))
. I have proposed a fix in #393
The default API uses
completion-styles
andcompletion-styles-alist
to configure completions. Also if the collection passed tocompleting-read
is a function it can return metadata to configure the completion behaviour (like forcompletion-in-region
#84). It would be nice if selectrum would support those settings.