Skip to content

Commit

Permalink
improvement: make inlayHints/resolve not depend on focused document
Browse files Browse the repository at this point in the history
  • Loading branch information
kasiaMarek committed Dec 10, 2024
1 parent ac01eef commit baf4180
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,9 @@ class Compilers(
if (d.kind() <= 2) InlayHintKind.Type
else InlayHintKind.Parameter
hint.setKind(kind)
hint.setData(Array(""))
hint.setData(
internal.pc.InlayHints.toData(params.uri(), List(Left("")))
)
hint
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import scala.concurrent.ExecutionContextExecutorService
import scala.concurrent.Future

import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.pc.InlayHints
import scala.meta.io.AbsolutePath
import scala.meta.pc.CancelToken

import com.google.gson.JsonArray
import com.google.gson.JsonElement
import org.eclipse.lsp4j.InlayHint
import org.eclipse.lsp4j.InlayHintLabelPart
import org.eclipse.lsp4j.TextDocumentIdentifier
Expand All @@ -19,21 +20,21 @@ final class InlayHintResolveProvider(
)(implicit ec: ExecutionContextExecutorService, rc: ReportContext) {
def resolve(
inlayHint: InlayHint,
path: AbsolutePath,
token: CancelToken,
): Future[InlayHint] = {
scala.util.Try {
val data = inlayHint.getData().asInstanceOf[JsonArray]
getLabelParts(inlayHint).zip(parseData(data))
}.toEither match {
case Right(labelParts) =>
resolve(inlayHint, labelParts, path, token)
case Left(error) =>
): Future[InlayHint] =
try {
val (uri, labelParts) =
InlayHints.fromData(inlayHint.getData().asInstanceOf[JsonElement])
pprint.log(uri)
val path = uri.toAbsolutePath
pprint.log(labelParts)
resolve(inlayHint, getLabelParts(inlayHint).zip(labelParts), path, token)
} catch {
case error: Throwable =>
scribe.warn(s"Failed to resolve inlay hint: $error")
rc.unsanitized.create(report(inlayHint, path, error), ifVerbose = true)
rc.unsanitized.create(report(inlayHint, error), ifVerbose = true)
Future.successful(inlayHint)
}
}

private def resolve(
inlayHint: InlayHint,
Expand Down Expand Up @@ -87,19 +88,6 @@ final class InlayHintResolveProvider(
case Right(labelParts) => labelParts.asScala.toList
}

val symbol = new JsonParser.Of[String]
val range = new JsonParser.Of[l.Position]

private def parseData(
data: JsonArray
): List[Either[String, l.Position]] = {
data.asScala.map {
case range.Jsonized(data) => Right(data)
case symbol.Jsonized(data) => Left(data)
case _ => Left("")
}.toList
}

private def getSymbol(symbol: String, path: AbsolutePath) = {
definitionProvider
.fromSymbol(symbol, Some(path))
Expand All @@ -109,7 +97,6 @@ final class InlayHintResolveProvider(

private def report(
inlayHint: InlayHint,
path: AbsolutePath,
error: Throwable,
) = {
val pos = inlayHint.getPosition()
Expand All @@ -119,9 +106,7 @@ final class InlayHintResolveProvider(
|
|inlayHint: $inlayHint
|""".stripMargin,
s"failed to resolve inlayHint in $path",
id = Some(s"$path::${pos.getLine()}:${pos.getCharacter()}"),
path = Some(path.toURI),
s"failed to resolve inlayHint",
error = Some(error),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1022,13 +1022,10 @@ abstract class MetalsLspService(

def inlayHintResolve(
inlayHint: InlayHint
): CompletableFuture[InlayHint] = {
): CompletableFuture[InlayHint] =
CancelTokens.future { token =>
focusedDocument
.map(path => inlayHintResolveProvider.resolve(inlayHint, path, token))
.getOrElse(Future.successful(inlayHint))
inlayHintResolveProvider.resolve(inlayHint, token)
}
}

override def documentHighlights(
params: TextDocumentPositionParams
Expand Down
55 changes: 45 additions & 10 deletions mtags-shared/src/main/scala/scala/meta/internal/pc/InlayHints.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package scala.meta.internal.pc

import java.net.URI

import scala.collection.mutable.ListBuffer

import scala.meta.internal.jdk.CollectionConverters._
import scala.meta.internal.mtags.CommonMtagsEnrichments.XtensionText

import com.google.gson.Gson
import com.google.gson.JsonElement
import org.eclipse.lsp4j.InlayHint
import org.eclipse.lsp4j.InlayHintKind
import org.eclipse.{lsp4j => l}

case class InlayHints(
uri: URI,
inlayHints: List[InlayHint],
definitions: Set[Int]
) {
Expand All @@ -34,20 +39,13 @@ case class InlayHints(
) = {
val hint = new InlayHint()
hint.setPosition(pos)
val (label, data) = labelParts.map(lp => (lp.label, getData(lp.data))).unzip
val (label, dataInfo) = labelParts.map(lp => (lp.label, lp.data)).unzip
hint.setLabel(label.asJava)
hint.setData(data.toArray)
hint.setData(InlayHints.toData(uri, dataInfo))
hint.setKind(kind)
hint
}

private def getData(data: Either[String, l.Position]): Any = {
data match {
case Left(str) => str
case Right(pos) => pos
}
}

// If method has both type parameter and implicit parameter, we want the type parameter decoration to be displayed first,
// but it's added second. This method adds the decoration to the right position in the list.
private def addInlayHint(inlayHint: InlayHint): List[InlayHint] = {
Expand All @@ -60,7 +58,8 @@ case class InlayHints(
}

object InlayHints {
def empty: InlayHints = InlayHints(Nil, Set.empty)
private val gson = new Gson()
def empty(uri: URI): InlayHints = InlayHints(uri, Nil, Set.empty)

/**
* Creates a label for inlay hint by inserting `parts` on correct positions in `tpeStr`.
Expand Down Expand Up @@ -96,4 +95,40 @@ object InlayHints {
buffer += LabelPart(tpeStr.substring(current, tpeStr.length))
buffer.toList.filter(_.name.nonEmpty)
}

def toData(uri: URI, data: List[Either[String, l.Position]]): JsonElement =
gson.toJsonTree(
InlineHintData(
uri,
data.map {
case Left(str) => LabelPartData("string", str, null)
case Right(pos) => LabelPartData("position", null, pos)
}.asJava
)
)

def fromData(json: JsonElement): (URI, List[Either[String, l.Position]]) = {
val data = gson.fromJson(json, classOf[InlineHintData])
(
data.uri,
data.labelParts.asScala.toList.map { part =>
part.dataType match {
case "position" => Right(part.position)
case "string" => Left(part.string)
}
}
)
}
}

final case class InlineHintData(
uri: URI,
labelParts: java.util.List[LabelPartData]
)

// "string" or "position"
final case class LabelPartData(
dataType: String,
string: String,
position: l.Position
)
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ final class PcInlayHintsProvider(

def provide(): List[InlayHint] =
treesInRange()
.flatMap(tpdTree => traverse(InlayHints.empty, tpdTree).result())
.flatMap(tpdTree =>
traverse(InlayHints.empty(params.uri()), tpdTree).result()
)

private def adjustPos(pos: Position): Position =
pos.adjust(text)._1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class PcInlayHintsProvider(
.headOption
.getOrElse(unit.tpdTree)
.enclosedChildren(pos.span)
.flatMap(tpdTree => deepFolder(InlayHints.empty, tpdTree).result())
.flatMap(tpdTree => deepFolder(InlayHints.empty(params.uri()), tpdTree).result())

private def adjustPos(pos: SourcePosition): SourcePosition =
pos.adjust(text)._1
Expand Down
13 changes: 8 additions & 5 deletions tests/mtest/src/main/scala/tests/TestInlayHints.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import scala.collection.mutable.ListBuffer
import scala.meta.internal.jdk.CollectionConverters._
import scala.meta.internal.metals.TextEdits
import scala.meta.internal.mtags.CommonMtagsEnrichments._
import scala.meta.internal.pc.InlayHints

import com.google.gson.JsonElement
import org.eclipse.lsp4j.InlayHint
import org.eclipse.lsp4j.TextEdit
import org.eclipse.{lsp4j => l}
Expand All @@ -32,7 +34,8 @@ object TestInlayHints {
case Right(labelParts) => labelParts.asScala.map(_.getValue()).toList
}
val data =
inlayHint.getData().asInstanceOf[Array[Any]]
InlayHints.fromData(inlayHint.getData().asInstanceOf[JsonElement])._2

buffer += "/*"
labels.zip(data).foreach { case (label, data) =>
buffer += label
Expand All @@ -42,11 +45,11 @@ object TestInlayHints {
buffer.toList.mkString
}

private def readData(data: Any): List[String] = {
private def readData(data: Either[String, l.Position]): List[String] = {
data match {
case data: String if data.isEmpty => Nil
case data: String => List("<<", data, ">>")
case data: l.Position =>
case Left("") => Nil
case Left(data) => List("<<", data, ">>")
case Right(data) =>
val str = s"(${data.getLine()}:${data.getCharacter()})"
List("<<", str, ">>")
}
Expand Down

0 comments on commit baf4180

Please sign in to comment.