Open adaszko opened 1 year ago
I really want this, has anyone implemented it before I spend time on it.
I don't think it's currently possible with nvim-cmp
I was just looking into this myself as I've been annoyed by this behavior for years (not just with nvim-cmp, but with literally every autocompletion system I've ever used), and I wonder if you might be able to hack something by tapping into the confirm_done
event. It would be a huge hack, but you could basically delete the redundantly inserted suffix after insertion by detecting the duplicate. My main concern with it would be that it would be clunky and error prone, so a new ConfirmBehavior
would probably be way more robust.
I don't think it's currently possible with nvim-cmp so this would be a feature request. Curious what you think.
Hi. Why you cannot use cmp.ConfirmBehavior.Replace
?
It seems works for me.
It seems works for me.
The difference is subtle, and I think it might be due to this code that defaults to replacing on keyword boundaries.
If you have this text in a buffer (cursor position shown with ^
) and you hit <Tab>
:
// Going to do autocompletion here...
FooBazzzz
^
// And elsewhere there's this completion candidate:
FooBarBaz
Insert
behavior, FooBazzzz
becomes FooBarBazBazzzz
Replace
behavior, FooBazzzz
becomes FooBarBaz
MatchSuffix
behavior, FooBazzzz
would become FooBarBazzzz
The example is contrived, of course, but hopefully it illustrates what the difference would be. Sometimes you really want to keep that zzz
suffix around, because you're about to continue editing it, and removing it is a destructive operation that you can't undo without also undoing the autocompletion.
In the abstract, I think the difference between the behaviors can be described as:
Insert
: "only ever add to the buffer, never remove anything" AKA "I don't mind having to delete trailing text after the completion sometimes".Replace
: "aggressively make the buffer match the completion, no matter what" AKA "I really hate having to delete trailing text after the completion, so do it for me unconditionally".MatchSuffix
: "try to preserve a trailing suffix that I might actually care about" AKA "I don't want to have to delete trailing text after the completion, but for the trivial cases where you can be sure I don't want it, get rid of it for me, and I'll handle it manually in the other cases".Does that make sense?
Hm.... If so, MatchSuffix
behavior is to edit the suffix after insertion?
And what is the cursor behavior after insertion when MatchSuffix
?
Hm.... If so,
MatchSuffix
behavior is to edit the suffix after insertion?And what is the cursor behavior after insertion when
MatchSuffix
?
Take a look at how zsh does it (ignore the trailing /
):
The cursor lands at the end of the matched suffix which I think is a good choice.
Thanks. I get it. If so, this is feature request.
Reading this thread has made me realize that there are actually two separate requests going on here.
'keyword'
.They're related because they're both about what nvim-cmp should do when it does "in-word" completion, but they are actually separate requests.
I think I might have a couple of examples that do a better job at illustrating what I'm asking for than my previous attempt. These are two scenarios that I ran into today and which show why Replace
doesn't quite do the right thing, but neither does Insert
, motivating the desire to have a MatchSuffix
option.
Example A:
<Tab>
(which is my mapping to accept the autocompletion) at this position: "(Kub<Tab>
Services → Clusters → Data centers"Replace
behavior activated, nvim-cmp changes the line to: "(Kubernetes → Clusters → Data centers" (ie. it blows away the trailing "Services", requiring me to type it again).Insert
behavior would have done what I wanted, and MatchSuffix
would have also; the line would contain "(KubernetesServices → Clusters → Data centers" and I'd just have to type the trailing ") ".Example B:
ProfilingData
and AggregateProfilingData
.ProfilingData
I should have written AggregateProfilingData
.Agg
.AggProfilingData
.<Tab>
at this position: Agg<Tab>ProfilingData
Replace
behavior does exactly what I want, changing the name to AggregateProfilingData
, and MatchSuffix
would have also.Insert
behavior would have done the wrong thing (producing AggregateProfilingDataProfilingData
) and I would have had to delete the unwanted suffix[^with].[^with]: With something like <C-[
(to leave INSERT
mode) and then dFP
.
I run into examples like this multiple times per day, and in my (subjective) experience:
Insert
behavior works great when appending (a common case), but when prepending (also a common case) it only sometimes does what I want.Replace
behavior also works great when appending, and when prepending (also a common case) it only often does what I want, but not always.MatchSuffix
behavior would work great when appending, and when prepending it would almost always do what I want.In practice, having to delete an unwanted suffix is a bit annoying, but it's not as painful as having to restore text which was deleted undesirably, so MatchSuffix
would avoid the pain that sometimes comes with Replace
, but it would frequently avoid the annoyance that can come with Insert
mode.
Sorry about the wall of text, but I couldn't find a more concise way to describe it! 🙇
The initial request is about having nvim-cmp recognize that the suffix helps disambiguate possible completions and having it select the resulting completion immediately without forcing the user to choose. That's what's https://github.com/hrsh7th/nvim-cmp/issues/1716#issuecomment-2237082589.
Hm... It seems suffix matcher
instead of confirming.
And it does not work for Example A.
In this instance Insert behavior would have done what I wanted, and MatchSuffix would have also; the line would contain "(KubernetesServices → Clusters → Data centers" and I'd just have to type the trailing ") ".
Well, it works well on Insert
, but it does not work well on MatchSuffix
.
Because the cursor position is:
(KubenetesServices|
<- here.
It is not (Kubernetes|Services
.
You need to modify the code in normal mode.
I think the cursor behavior is complex.
The cursor lands at the end of the matched suffix which I think is a good choice.
If so,
With the hypothetical MatchSuffix behavior, FooBazzzz would become FooBarBazzzz
The cursor position is:
FooBarBaz|zzz
It is true?
Please describe it for me.
With the hypothetical MatchSuffix behavior, FooBazzzz would become FooBarBazzzz
The cursor position is:
FooBarBaz|zzz
It is true?
Good question. @wincent and I seem to be proposing two conflicting specifications here.
@wincent's: Foo|<TAB>Bazzzz
-> FooBarBaz|zzz
, assuming available completion FooBarBaz
.
Me: Foo|<TAB>Bazzzz
-> NO CHANGE! Why? Because completions are only offered if the suffix exhausts the completed keyword fully, so it would only complete if we were completing Foo|<TAB>Baz
, not Foo|<TAB>Bazzzz
.
So what I'm proposing is more restrictive but if you chose a completion at some point, you can be sure the keyword isn't misspelled. It also feels less surprising. What's important, such behaviour still works in both Example A and B.
In this instance Insert behavior would have done what I wanted, and MatchSuffix would have also; the line would contain "(KubernetesServices → Clusters → Data centers" and I'd just have to type the trailing ") ".
Well, it works well on
Insert
, but it does not work well onMatchSuffix
.Because the cursor position is:
(KubenetesServices|
<- here.It is not
(Kubernetes|Services
.You need to modify the code in normal mode.
I would actually think that it would be (Kubernetes|Services
; given that it didn't complete Services
, it shouldn't jump to the end of that.
The cursor position is:
FooBarBaz|zzz
It is true?
That is what I'd intuitively expect.
Here's one more thing to consider: the interaction of the different behaviors with the experimental "ghost text" feature. At the moment, I believe the ghost text always shows what would be inserted with Insert
behavior; it doesn't show only the part of the completion that would appear in the buffer if a Replace
behavior completion was accepted, not does it show what would be inserted if we added this hypothetical MatchSuffix
behavior. I don't think that's a deal breaker, but in an ideal world, ghost text would show as close as possible to what's going to be added to the buffer if you accept a completion.
So what I'm proposing is more restrictive but if you chose a completion at some point, you can be sure the keyword isn't misspelled. It also feels less surprising. What's important, such behaviour still works in both Example A and B.
Thanks for bringing this up; I hadn't noticed this detail about your proposal (and I'd have to think a while before forming an opinion about which variant is better).
So what I'm proposing is more restrictive but if you chose a completion at some point, you can be sure the keyword isn't misspelled. It also feels less surprising. What's important, such behaviour still works in both Example A and B.
OK. So it work for Example B, but not A.
Because Kubernetes
not matched to Services
.
I would actually think that it would be (Kubernetes|Services; given that it didn't complete Services, it shouldn't jump to the end of that.
It jumps to the end when it is matched only the suffix is matched? It is OK.
The example is contrived, of course, but hopefully it illustrates what the difference would be. Sometimes you really want to keep that zzz suffix around, because you're about to continue editing it, and removing it is a destructive operation that you can't undo without also undoing the autocompletion.
But this is wrong?
@adaszko : Complete candidate only matched to suffix @wincent : Insert candidate if not matched to suffix, Replace the candidate if it is matched to suffix
Hm... They are conflicted desire.
So what I'm proposing is more restrictive but if you chose a completion at some point, you can be sure the keyword isn't misspelled. It also feels less surprising. What's important, such behaviour still works in both Example A and B.
OK. So it work for Example B, but not A. Because
Kubernetes
not matched toServices
.
That's true. My mistake. In Example A (Kub<Tab>Services
) you would have to manually insert a space before Services
so that Kub<Tab> Services
gets completed to Kubernetes Services
.
Here's a thought: To make Example A work, ConfirmBehavior.MatchSuffix
could first try to perform a restrictive match on the suffix as I described above, and if it doesn't match any candidates, perform ConfirmBehavior.Insert
as a fallback.
Yes. It is @wincent 's idea.
Great, so we have an agreement on the specification then?
Here's a thought: To make Example A work, ConfirmBehavior.MatchSuffix could first try to perform a restrictive match on the suffix as I described above, and if it doesn't match any candidates, perform ConfirmBehavior.Insert as a fallback.
If the behavior is OK, Yes.
Here's a thought: To make Example A work, ConfirmBehavior.MatchSuffix could first try to perform a restrictive match on the suffix as I described above, and if it doesn't match any candidates, perform ConfirmBehavior.Insert as a fallback.
If the behavior is OK, Yes.
Yes, such behaviour totally works for me.
FWIW, I realised zsh can be configured to behave like that too:
setopt complete_in_word
zstyle ':completion:*' completer _complete _prefix
Hi and thank you for this fantastic plugin!
I'm looking for a middle ground between
cmp.ConfirmBehavior.Replace
andcmp.ConfirmBehavior.Insert
. Namely, a one that would completefoo_<TAB>_baz
tofoo_bar_baz
right away given possible completionsfoo_bar_baz
andfoo_bar_quux
. It would ignorefoo_bar_quux
automatically by noticing it doesn't match the existing_baz
suffix. This is just like zsh'scomplete_in_word
option.I currently use
cmp.ConfirmBehavior.Insert
but have to constantly re-edit the suffix after inserting something in the middle of a word.I don't think it's currently possible with nvim-cmp so this would be a feature request. Curious what you think.