-
Notifications
You must be signed in to change notification settings - Fork 346
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
Respect client capabilities for completion item snippets #1057
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking into this @gabro ! It long overdue, since I totally forgot about this.
There are some more snippets in Completions.scala
we need to change. Also, we should probably disable fillAllFields
completion without snippets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot click the right checkbox :O I shouldn't work so late I think 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for looking into this!
metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala
Show resolved
Hide resolved
mtags/src/main/scala/scala/meta/internal/pc/CompletionProvider.scala
Outdated
Show resolved
Hide resolved
Hello everyone. I tried out this patch briefly with ycmd and completions started working. @gabro Thanks again for working on this. |
@tgodzik thanks for the review and woops, yes, I've missed a lot of cases from
Thanks for testing this, @bstaletic! |
@@ -985,7 +989,7 @@ trait Completions { this: MetalsGlobal => | |||
allParams.exists(param => param.name.startsWith(prefix)) | |||
val isExplicitlyCalled = suffix.startsWith(prefix) | |||
val hasParamsToFill = allParams.count(!_.hasDefault) > 1 | |||
if ((shouldShow || isExplicitlyCalled) && hasParamsToFill) { | |||
if ((shouldShow || isExplicitlyCalled) && hasParamsToFill && clientSupportsSnippets) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this disables the Autofill with default values
completion when the client does not support snippets, as per @tgodzik. Please review that it makes sense and that we can't find a good completion that works without snippets
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's disable it for now, I will think it over to see if it makes sense without the snippets.
if (clientSupportsSnippets) { | ||
"match {\n\tcase$0\n}" | ||
} else { | ||
"match" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
avoid the braces if we have no snippets. Inserting braces will result in a cursor out of place ({}<CURSOR>
), which seems bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my point of view, it is bad to end up with foo{}<CURSOR>
after completion. I ran into a similar case with Apple's swift server and it forced me to do this - overriding completion algorithm for swift to strip ()
from foo()
, even if there's something in between (
and )
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the feedback! I'm actively avoiding that pattern, except in some cases where the completion is still very useful and/or it's opt-in (e.g. it's not the default case)
if (clientSupportsSnippets) { | ||
"match {\n\t${head.edit.getNewText} $$0\n\t" | ||
} else { | ||
"match {\n\t${head.edit.getNewText}\n\t" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in this case we're ok with an "annoying" cursor position (match {...}<CURSOR>
) since the exhaustive match feature is quite useful regardless (and it's opt-in anyway)
new l.TextEdit(editRange, "case ($0) =>"), | ||
new l.TextEdit( | ||
editRange, | ||
if (clientSupportsSnippets) "case ($0) =>" else "case () =>" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not brilliant, but again, it's opt-in.
@bstaletic curious to hear your input about one particular case: Scala allows interpolating strings with params and if you have a "simple" param you can do // from
s"$buffer.app<CURSOR>"
//to
s"${buffer.append(<CURSOR>)}" This is not possible without snippets, so what would you prefer as a language client?
I'm leaning towards 1 but the current implementation in this PR would yield 2 |
I'm leaning towards 1 as well. If a user complains that such a case isn't getting completions I can point the user to the logs and say no completions are returned by metals. The option 2 doesn't seem good for the same reason Tell me if I understand option 3 and scala parsing rules correctly. Without braces @puremourning Do you have anything to add? |
To add to the above, I won't mind option 2 either. If option 1 is too much trouble, oh well, I won't lose any sleep over it. |
Correct. Thanks for the feedback @bstaletic, I'll see whether option 1 is worth the hassle :) |
Idiomatically it should be:
Thought I think it would be reasonable to only support the first one. ycmd would handle the correct prefix selection (assuming compliant Basically, I don't think you should insert anything to the "right" for the word being completed, ever. Some servers love to try and insert IM(ns?)HO ! |
Thanks for the feedback, that's the general direction I'm following when there's no snippet support in the client. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @gabro for fixing this! LGTM
@@ -985,7 +989,7 @@ trait Completions { this: MetalsGlobal => | |||
allParams.exists(param => param.name.startsWith(prefix)) | |||
val isExplicitlyCalled = suffix.startsWith(prefix) | |||
val hasParamsToFill = allParams.count(!_.hasDefault) > 1 | |||
if ((shouldShow || isExplicitlyCalled) && hasParamsToFill) { | |||
if ((shouldShow || isExplicitlyCalled) && hasParamsToFill && clientSupportsSnippets) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's disable it for now, I will think it over to see if it makes sense without the snippets.
@puremourning / @bstaletic in the end, is quite finicky to tweak that particular use case, and it's quite a niche use case so - for now - I'll merge this as is (option 2 above) |
Fixes #1055
Previously we always used snippets in completion items, regardless of whether the client declared that it supported them. Now we respect the capabilities declared by the client.