From 5c4d2a76fead43bfe639981674a3e0e09bd0d588 Mon Sep 17 00:00:00 2001
From: scarf <greenscarf005@gmail.com>
Date: Sat, 30 Nov 2024 14:11:25 +0900
Subject: [PATCH] perf: migrate to `codeAction/resolve`

---
 .../FilterMapToCollectCodeAction.scala        | 59 +++++++++++++++----
 1 file changed, 48 insertions(+), 11 deletions(-)

diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/FilterMapToCollectCodeAction.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/FilterMapToCollectCodeAction.scala
index ca7164510df..7d3c0d9f39d 100644
--- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/FilterMapToCollectCodeAction.scala
+++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/FilterMapToCollectCodeAction.scala
@@ -4,6 +4,7 @@ import scala.concurrent.ExecutionContext
 import scala.concurrent.Future
 
 import scala.meta._
+import scala.meta.internal.metals.JsonParser._
 import scala.meta.internal.metals.MetalsEnrichments._
 import scala.meta.internal.metals.codeactions.CodeAction
 import scala.meta.internal.metals.codeactions.CodeActionBuilder
@@ -16,19 +17,62 @@ import org.eclipse.{lsp4j => l}
 class FilterMapToCollectCodeAction(trees: Trees) extends CodeAction {
   override def kind: String = l.CodeActionKind.RefactorRewrite
 
+  private case class FilterMapCollectParams(
+      param: l.TextDocumentIdentifier,
+      pos: l.Position,
+  )
+
+  override def resolveCodeAction(codeAction: l.CodeAction, token: CancelToken)(
+      implicit ec: ExecutionContext
+  ): Option[Future[l.CodeAction]] = {
+    println(codeAction.getData.toJson)
+    val edits = for {
+      data <- codeAction.getData.toJson.as[FilterMapCollectParams].toOption
+      params = data.param
+      uri = params.getUri()
+      path = uri.toAbsolutePath
+    } yield trees
+      .findLastEnclosingAt[Term.Apply](path, data.pos)
+      .flatMap(findFilterMapChain)
+      .map(toTextEdit(_))
+      .map(edit => List(uri -> List(edit)))
+      .getOrElse(Nil)
+
+    edits match {
+      case None | (Some(Nil)) => None
+      case Some(xs) => {
+        val workspaceEdit = new l.WorkspaceEdit(
+          xs.map { case (uri, edits) => uri -> edits.asJava }.toMap.asJava
+        )
+        codeAction.setEdit(workspaceEdit)
+        Some(Future.successful(codeAction))
+      }
+    }
+  }
+
   override def contribute(params: CodeActionParams, token: CancelToken)(implicit
       ec: ExecutionContext
   ): Future[Seq[l.CodeAction]] = Future {
     val uri = params.getTextDocument().getUri()
 
     val path = uri.toAbsolutePath
-    val range = params.getRange()
+    val start = params.getRange.getStart
 
     trees
-      .findLastEnclosingAt[Term.Apply](path, range.getStart())
+      .findLastEnclosingAt[Term.Apply](path, start)
       .flatMap(findFilterMapChain)
-      .map(toTextEdit(_))
-      .map(toCodeAction(uri, _))
+      .map(_ => {
+        val data =
+          FilterMapCollectParams(
+            params.getTextDocument(),
+            start,
+          )
+        CodeActionBuilder.build(
+          title = FilterMapToCollectCodeAction.title,
+          kind = this.kind,
+          data = Some(data.toJsonObject),
+        )
+      })
       .toSeq
   }
 
@@ -66,13 +110,6 @@ class FilterMapToCollectCodeAction(trees: Trees) extends CodeAction {
     new l.TextEdit(chain.pos.toLsp, indented)
   }
 
-  private def toCodeAction(uri: String, textEdit: l.TextEdit): l.CodeAction =
-    CodeActionBuilder.build(
-      title = FilterMapToCollectCodeAction.title,
-      kind = this.kind,
-      changes = List(uri.toAbsolutePath -> List(textEdit)),
-    )
-
   private implicit class FunctionOps(fn: Term.Function) {
     def renameParam(to: Term.Name): Term = {
       val fnParamName = fn.params.head.name.value