Skip to content

Commit

Permalink
Respect client capabilities for completion item snippets
Browse files Browse the repository at this point in the history
  • Loading branch information
gabro committed Nov 10, 2019
1 parent f54ca49 commit 2c40a21
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 9 deletions.
10 changes: 8 additions & 2 deletions metals/src/main/scala/scala/meta/internal/metals/Compilers.scala
Expand Up @@ -6,6 +6,7 @@ import ch.epfl.scala.bsp4j.ScalaBuildTarget
import ch.epfl.scala.bsp4j.ScalacOptionsItem
import java.util.Collections
import java.util.concurrent.ScheduledExecutorService
import org.eclipse.lsp4j.InitializeParams
import org.eclipse.lsp4j.CompletionItem
import org.eclipse.lsp4j.CompletionList
import org.eclipse.lsp4j.CompletionParams
Expand Down Expand Up @@ -40,7 +41,8 @@ class Compilers(
search: SymbolSearch,
embedded: Embedded,
statusBar: StatusBar,
sh: ScheduledExecutorService
sh: ScheduledExecutorService,
initializeParams: Option[InitializeParams]
)(implicit ec: ExecutionContextExecutorService)
extends Cancelable {
val plugins = new CompilerPlugins()
Expand Down Expand Up @@ -247,7 +249,11 @@ class Compilers(
.withExecutorService(ec)
.withScheduledExecutorService(sh)
.withConfiguration(
config.compilers.copy(_symbolPrefixes = userConfig().symbolPrefixes)
config.compilers.copy(
_symbolPrefixes = userConfig().symbolPrefixes,
isCompletionSnippetsEnabled =
initializeParams.supportsCompletionSnippets
)
)

def newCompiler(
Expand Down
Expand Up @@ -509,6 +509,16 @@ object MetalsEnrichments
)
} yield hierarchicalDocumentSymbolSupport.booleanValue).getOrElse(false)

def supportsCompletionSnippets: Boolean =
(for {
params <- initializeParams
capabilities <- Option(params.getCapabilities)
textDocument <- Option(capabilities.getTextDocument)
completion <- Option(textDocument.getCompletion)
completionItem <- Option(completion.getCompletionItem)
snippetSupport <- Option(completionItem.getSnippetSupport())
} yield snippetSupport.booleanValue).getOrElse(false)

}

implicit class XtensionPromise[T](promise: Promise[T]) {
Expand Down
Expand Up @@ -381,7 +381,8 @@ class MetalsLanguageServer(
),
embedded,
statusBar,
sh
sh,
Option(params)
)
)
doctor = new Doctor(
Expand Down
Expand Up @@ -64,6 +64,11 @@ enum OverrideDefFormat {
*/
boolean isSignatureHelpDocumentationEnabled();

/**
* Returns true if completions can contain snippets.
*/
boolean isCompletionSnippetsEnabled();

/**
* The maximum delay for requests to respond.
*
Expand Down
Expand Up @@ -16,7 +16,8 @@ import org.eclipse.{lsp4j => l}

class CompletionProvider(
val compiler: MetalsGlobal,
params: OffsetParams
params: OffsetParams,
clientSupportsSnippets: Boolean
) {
import compiler._

Expand Down Expand Up @@ -88,13 +89,13 @@ class CompletionProvider(
item.setDetail(detail)
}
val templateSuffix =
if (!isSnippet) ""
if (!isSnippet || !clientSupportsSnippets) ""
else if (completion.isNew &&
r.sym.dealiased.requiresTemplateCurlyBraces) " {}"
else ""

val typeSuffix =
if (!isSnippet) ""
if (!isSnippet || !clientSupportsSnippets) ""
else if (completion.isType && r.sym.hasTypeParams) "[$0]"
else if (completion.isNew && r.sym.hasTypeParams) "[$0]"
else ""
Expand All @@ -111,7 +112,11 @@ class CompletionProvider(
item.setFilterText(symbolName)
}

item.setInsertTextFormat(InsertTextFormat.Snippet)
if (clientSupportsSnippets) {
item.setInsertTextFormat(InsertTextFormat.Snippet)
} else {
item.setInsertTextFormat(InsertTextFormat.PlainText)
}

r match {
case i: TextEditMember =>
Expand Down Expand Up @@ -151,7 +156,9 @@ class CompletionProvider(
case head :: Nil if head.forall(_.isImplicit) =>
() // Don't set ($0) snippet for implicit-only params.
case _ =>
item.setTextEdit(textEdit(baseLabel + "($0)"))
if (clientSupportsSnippets) {
item.setTextEdit(textEdit(baseLabel + "($0)"))
}
metalsConfig
.parameterHintsCommand()
.asScala
Expand Down
Expand Up @@ -18,6 +18,7 @@ case class PresentationCompilerConfigImpl(
isCompletionItemDocumentationEnabled: Boolean = true,
isHoverDocumentationEnabled: Boolean = true,
isSignatureHelpDocumentationEnabled: Boolean = true,
isCompletionSnippetsEnabled: Boolean = true,
isCompletionItemResolve: Boolean = true,
timeoutDelay: Long = 20,
timeoutUnit: TimeUnit = TimeUnit.SECONDS
Expand Down
Expand Up @@ -85,7 +85,8 @@ case class ScalaPresentationCompiler(
params: OffsetParams
): CompletableFuture[CompletionList] =
access.withInterruptableCompiler(emptyCompletion, params.token) { global =>
new CompletionProvider(global, params).completions()
new CompletionProvider(global, params, config.isCompletionSnippetsEnabled)
.completions()
}

// NOTE(olafur): hover and signature help use a "shared" compiler instance because
Expand Down
@@ -0,0 +1,76 @@
package tests.pc

import tests.BaseCompletionSuite
import scala.meta.pc.PresentationCompilerConfig
import scala.meta.internal.pc.PresentationCompilerConfigImpl

object CompletionSnippetNegSuite extends BaseCompletionSuite {

override def config: PresentationCompilerConfig =
PresentationCompilerConfigImpl(
isCompletionSnippetsEnabled = false
)

checkSnippet(
"member",
"""
|object Main {
| List.appl@@
|}
|""".stripMargin,
"""|apply
|unapplySeq
|""".stripMargin,
compat = Map(
"2.13" ->
// the second apply is from scala/collection/BuildFrom#apply(), introduced in 2.13
"""|apply
|unapplySeq
|apply
|""".stripMargin
)
)

checkSnippet(
"scope",
"""
|object Main {
| printl@@
|
|}
|""".stripMargin,
"""|println()
|println
|""".stripMargin
)

checkSnippet(
"java-nullary",
"""
|class Foo {
| override def toString = "Foo"
|}
|object Main {
| new Foo().toStrin@@
|
|}
|""".stripMargin,
// even if `Foo.toString` is nullary, it overrides `Object.toString()`
// which is a Java non-nullary method with an empty parameter list.
"""|toString()
|""".stripMargin
)

checkSnippet(
"type",
s"""|object Main {
| val x: scala.IndexedSe@@
|}
|""".stripMargin,
// It's expected to have two separate results, one for `object IndexedSeq` and one for `type IndexedSeq[T]`.
"""|IndexedSeq
|IndexedSeq
|""".stripMargin
)

}

0 comments on commit 2c40a21

Please sign in to comment.