Skip to content

Commit 2c40a21

Browse files
committed
Respect client capabilities for completion item snippets
1 parent f54ca49 commit 2c40a21

File tree

8 files changed

+116
-9
lines changed

8 files changed

+116
-9
lines changed

metals/src/main/scala/scala/meta/internal/metals/Compilers.scala

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ch.epfl.scala.bsp4j.ScalaBuildTarget
66
import ch.epfl.scala.bsp4j.ScalacOptionsItem
77
import java.util.Collections
88
import java.util.concurrent.ScheduledExecutorService
9+
import org.eclipse.lsp4j.InitializeParams
910
import org.eclipse.lsp4j.CompletionItem
1011
import org.eclipse.lsp4j.CompletionList
1112
import org.eclipse.lsp4j.CompletionParams
@@ -40,7 +41,8 @@ class Compilers(
4041
search: SymbolSearch,
4142
embedded: Embedded,
4243
statusBar: StatusBar,
43-
sh: ScheduledExecutorService
44+
sh: ScheduledExecutorService,
45+
initializeParams: Option[InitializeParams]
4446
)(implicit ec: ExecutionContextExecutorService)
4547
extends Cancelable {
4648
val plugins = new CompilerPlugins()
@@ -247,7 +249,11 @@ class Compilers(
247249
.withExecutorService(ec)
248250
.withScheduledExecutorService(sh)
249251
.withConfiguration(
250-
config.compilers.copy(_symbolPrefixes = userConfig().symbolPrefixes)
252+
config.compilers.copy(
253+
_symbolPrefixes = userConfig().symbolPrefixes,
254+
isCompletionSnippetsEnabled =
255+
initializeParams.supportsCompletionSnippets
256+
)
251257
)
252258

253259
def newCompiler(

metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,16 @@ object MetalsEnrichments
509509
)
510510
} yield hierarchicalDocumentSymbolSupport.booleanValue).getOrElse(false)
511511

512+
def supportsCompletionSnippets: Boolean =
513+
(for {
514+
params <- initializeParams
515+
capabilities <- Option(params.getCapabilities)
516+
textDocument <- Option(capabilities.getTextDocument)
517+
completion <- Option(textDocument.getCompletion)
518+
completionItem <- Option(completion.getCompletionItem)
519+
snippetSupport <- Option(completionItem.getSnippetSupport())
520+
} yield snippetSupport.booleanValue).getOrElse(false)
521+
512522
}
513523

514524
implicit class XtensionPromise[T](promise: Promise[T]) {

metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,8 @@ class MetalsLanguageServer(
381381
),
382382
embedded,
383383
statusBar,
384-
sh
384+
sh,
385+
Option(params)
385386
)
386387
)
387388
doctor = new Doctor(

mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompilerConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ enum OverrideDefFormat {
6464
*/
6565
boolean isSignatureHelpDocumentationEnabled();
6666

67+
/**
68+
* Returns true if completions can contain snippets.
69+
*/
70+
boolean isCompletionSnippetsEnabled();
71+
6772
/**
6873
* The maximum delay for requests to respond.
6974
*

mtags/src/main/scala/scala/meta/internal/pc/CompletionProvider.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import org.eclipse.{lsp4j => l}
1616

1717
class CompletionProvider(
1818
val compiler: MetalsGlobal,
19-
params: OffsetParams
19+
params: OffsetParams,
20+
clientSupportsSnippets: Boolean
2021
) {
2122
import compiler._
2223

@@ -88,13 +89,13 @@ class CompletionProvider(
8889
item.setDetail(detail)
8990
}
9091
val templateSuffix =
91-
if (!isSnippet) ""
92+
if (!isSnippet || !clientSupportsSnippets) ""
9293
else if (completion.isNew &&
9394
r.sym.dealiased.requiresTemplateCurlyBraces) " {}"
9495
else ""
9596

9697
val typeSuffix =
97-
if (!isSnippet) ""
98+
if (!isSnippet || !clientSupportsSnippets) ""
9899
else if (completion.isType && r.sym.hasTypeParams) "[$0]"
99100
else if (completion.isNew && r.sym.hasTypeParams) "[$0]"
100101
else ""
@@ -111,7 +112,11 @@ class CompletionProvider(
111112
item.setFilterText(symbolName)
112113
}
113114

114-
item.setInsertTextFormat(InsertTextFormat.Snippet)
115+
if (clientSupportsSnippets) {
116+
item.setInsertTextFormat(InsertTextFormat.Snippet)
117+
} else {
118+
item.setInsertTextFormat(InsertTextFormat.PlainText)
119+
}
115120

116121
r match {
117122
case i: TextEditMember =>
@@ -151,7 +156,9 @@ class CompletionProvider(
151156
case head :: Nil if head.forall(_.isImplicit) =>
152157
() // Don't set ($0) snippet for implicit-only params.
153158
case _ =>
154-
item.setTextEdit(textEdit(baseLabel + "($0)"))
159+
if (clientSupportsSnippets) {
160+
item.setTextEdit(textEdit(baseLabel + "($0)"))
161+
}
155162
metalsConfig
156163
.parameterHintsCommand()
157164
.asScala

mtags/src/main/scala/scala/meta/internal/pc/PresentationCompilerConfigImpl.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ case class PresentationCompilerConfigImpl(
1818
isCompletionItemDocumentationEnabled: Boolean = true,
1919
isHoverDocumentationEnabled: Boolean = true,
2020
isSignatureHelpDocumentationEnabled: Boolean = true,
21+
isCompletionSnippetsEnabled: Boolean = true,
2122
isCompletionItemResolve: Boolean = true,
2223
timeoutDelay: Long = 20,
2324
timeoutUnit: TimeUnit = TimeUnit.SECONDS

mtags/src/main/scala/scala/meta/internal/pc/ScalaPresentationCompiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ case class ScalaPresentationCompiler(
8585
params: OffsetParams
8686
): CompletableFuture[CompletionList] =
8787
access.withInterruptableCompiler(emptyCompletion, params.token) { global =>
88-
new CompletionProvider(global, params).completions()
88+
new CompletionProvider(global, params, config.isCompletionSnippetsEnabled)
89+
.completions()
8990
}
9091

9192
// NOTE(olafur): hover and signature help use a "shared" compiler instance because
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package tests.pc
2+
3+
import tests.BaseCompletionSuite
4+
import scala.meta.pc.PresentationCompilerConfig
5+
import scala.meta.internal.pc.PresentationCompilerConfigImpl
6+
7+
object CompletionSnippetNegSuite extends BaseCompletionSuite {
8+
9+
override def config: PresentationCompilerConfig =
10+
PresentationCompilerConfigImpl(
11+
isCompletionSnippetsEnabled = false
12+
)
13+
14+
checkSnippet(
15+
"member",
16+
"""
17+
|object Main {
18+
| List.appl@@
19+
|}
20+
|""".stripMargin,
21+
"""|apply
22+
|unapplySeq
23+
|""".stripMargin,
24+
compat = Map(
25+
"2.13" ->
26+
// the second apply is from scala/collection/BuildFrom#apply(), introduced in 2.13
27+
"""|apply
28+
|unapplySeq
29+
|apply
30+
|""".stripMargin
31+
)
32+
)
33+
34+
checkSnippet(
35+
"scope",
36+
"""
37+
|object Main {
38+
| printl@@
39+
|
40+
|}
41+
|""".stripMargin,
42+
"""|println()
43+
|println
44+
|""".stripMargin
45+
)
46+
47+
checkSnippet(
48+
"java-nullary",
49+
"""
50+
|class Foo {
51+
| override def toString = "Foo"
52+
|}
53+
|object Main {
54+
| new Foo().toStrin@@
55+
|
56+
|}
57+
|""".stripMargin,
58+
// even if `Foo.toString` is nullary, it overrides `Object.toString()`
59+
// which is a Java non-nullary method with an empty parameter list.
60+
"""|toString()
61+
|""".stripMargin
62+
)
63+
64+
checkSnippet(
65+
"type",
66+
s"""|object Main {
67+
| val x: scala.IndexedSe@@
68+
|}
69+
|""".stripMargin,
70+
// It's expected to have two separate results, one for `object IndexedSeq` and one for `type IndexedSeq[T]`.
71+
"""|IndexedSeq
72+
|IndexedSeq
73+
|""".stripMargin
74+
)
75+
76+
}

0 commit comments

Comments
 (0)