diff --git a/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala b/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala index dab4cbae11c..bc4a5b3e9bc 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala @@ -878,6 +878,34 @@ class Compilers( } }.getOrElse(Future.successful(Nil.asJava)) + def codeAction( + params: TextDocumentPositionParams, + token: CancelToken, + codeActionId: String, + codeActionPayload: Object, + ): Future[ju.List[TextEdit]] = { + withPCAndAdjustLsp(params) { (pc, pos, adjust) => + pc.codeAction( + CompilerOffsetParamsUtils.fromPos( + pos, + token, + outlineFilesProvider.getOutlineFiles(pc.buildTargetId()), + ), + codeActionId, + codeActionPayload, + ).asScala + .map { edits => + adjust.adjustTextEdits(edits) + } + } + }.getOrElse(Future.successful(Nil.asJava)) + + def supportedCodeActions(path: AbsolutePath): ju.List[String] = { + loadCompiler(path).map { pc => + pc.supportedCodeActions() + } + }.getOrElse(Nil.asJava) + def hover( params: HoverExtParams, token: CancelToken, diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeAction.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeAction.scala index 9a1d59c2a39..79a6f55325f 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeAction.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeAction.scala @@ -18,6 +18,12 @@ trait CodeAction { */ def kind: String + /** + * The CodeActionId for this code action, if applicable. CodeActionId is only + * used for code actions that require the use of the presentation compiler. + */ + def maybeCodeActionId: Option[String] = None + type CommandData type ActionCommand = ParametrizedCommand[CommandData] def command: Option[ActionCommand] = None diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala index 340841e91b7..c084717014a 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/CodeActionProvider.scala @@ -4,6 +4,7 @@ import scala.concurrent.ExecutionContext import scala.concurrent.Future import scala.jdk.CollectionConverters._ +import scala.meta.internal.metals.MetalsEnrichments.XtensionString import scala.meta.internal.metals._ import scala.meta.internal.metals.clients.language.MetalsLanguageClient import scala.meta.internal.metals.codeactions.CodeAction @@ -32,27 +33,15 @@ final class CodeActionProvider( new ActionableDiagnostic(), new StringActions(buffers), extractMemberAction, - new SourceOrganizeImports( - scalafixProvider, - buildTargets, - diagnostics, - ), - new OrganizeImportsQuickFix( - scalafixProvider, - buildTargets, - diagnostics, - ), + new SourceOrganizeImports(scalafixProvider, buildTargets, diagnostics), + new OrganizeImportsQuickFix(scalafixProvider, buildTargets, diagnostics), new InsertInferredType(trees, compilers, languageClient), new PatternMatchRefactor(trees), new RewriteBracesParensCodeAction(trees), new ExtractValueCodeAction(trees, buffers), new CreateCompanionObjectCodeAction(trees, buffers), new ExtractMethodCodeAction(trees, compilers, languageClient), - new InlineValueCodeAction( - trees, - compilers, - languageClient, - ), + new InlineValueCodeAction(trees, compilers, languageClient), new ConvertToNamedArguments(trees, compilers, languageClient), new FlatMapToForComprehensionCodeAction(trees, buffers), new MillifyDependencyCodeAction(buffers), @@ -60,6 +49,12 @@ final class CodeActionProvider( new ConvertCommentCodeAction(buffers), ) + def actionsForParams(params: l.CodeActionParams): List[CodeAction] = { + val path = params.getTextDocument.getUri.toAbsolutePath + val supportedCodeActions = compilers.supportedCodeActions(path) + allActions.filter(_.maybeCodeActionId.forall(supportedCodeActions.contains)) + } + def codeActions( params: l.CodeActionParams, token: CancelToken, @@ -73,7 +68,7 @@ final class CodeActionProvider( case None => true } - val actions = allActions.collect { + val actions = actionsForParams(params).collect { case action if isRequestedKind(action) => action.contribute(params, token) } diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/ConvertToNamedArguments.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/ConvertToNamedArguments.scala index 69180711dff..dcd5ed10041 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/ConvertToNamedArguments.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/ConvertToNamedArguments.scala @@ -28,6 +28,10 @@ class ConvertToNamedArguments( import ConvertToNamedArguments._ override val kind: String = l.CodeActionKind.RefactorRewrite + override val maybeCodeActionId: Option[String] = Some( + "ConvertToNamedArguments" + ) + override type CommandData = ServerCommands.ConvertToNamedArgsRequest override def command: Option[ActionCommand] = Some( @@ -57,7 +61,7 @@ class ConvertToNamedArguments( } yield () } - def getTermWithArgs( + private def getTermWithArgs( apply: Tree, args: List[Tree], nameEnd: Int, @@ -80,7 +84,7 @@ class ConvertToNamedArguments( } } - def firstApplyWithUnnamedArgs( + private def firstApplyWithUnnamedArgs( term: Option[Tree] ): Option[ApplyTermWithArgIndices] = { term match { diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/ExtractMethodCodeAction.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/ExtractMethodCodeAction.scala index 57a5a4ac89e..0eed8da0bd2 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/ExtractMethodCodeAction.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/ExtractMethodCodeAction.scala @@ -32,6 +32,10 @@ class ExtractMethodCodeAction( ) override def kind: String = l.CodeActionKind.RefactorExtract + override val maybeCodeActionId: Option[String] = Some( + "ExtractMethod" + ) + override def handleCommand( data: ServerCommands.ExtractMethodParams, token: CancelToken, diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/ImplementAbstractMembers.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/ImplementAbstractMembers.scala index e19519db98a..8ac155cc812 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/ImplementAbstractMembers.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/ImplementAbstractMembers.scala @@ -15,6 +15,10 @@ class ImplementAbstractMembers(compilers: Compilers) extends CodeAction { override def kind: String = l.CodeActionKind.QuickFix + override val maybeCodeActionId: Option[String] = Some( + "ImplementAbstractMembers" + ) + override def contribute( params: l.CodeActionParams, token: CancelToken, diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/ImportMissingSymbol.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/ImportMissingSymbol.scala index 2b192473917..baf503c2573 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/ImportMissingSymbol.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/ImportMissingSymbol.scala @@ -18,6 +18,10 @@ class ImportMissingSymbol(compilers: Compilers, buildTargets: BuildTargets) override def kind: String = l.CodeActionKind.QuickFix + override val maybeCodeActionId: Option[String] = Some( + "ImportMissingSymbol" + ) + override def contribute( params: l.CodeActionParams, token: CancelToken, diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/InlineValueCodeAction.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/InlineValueCodeAction.scala index 30350144479..6eccd72a2c0 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/InlineValueCodeAction.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/InlineValueCodeAction.scala @@ -32,6 +32,10 @@ class InlineValueCodeAction( override def kind: String = l.CodeActionKind.RefactorInline + override val maybeCodeActionId: Option[String] = Some( + "InlineValue" + ) + override def contribute(params: l.CodeActionParams, token: CancelToken)( implicit ec: ExecutionContext ): Future[Seq[l.CodeAction]] = Future { diff --git a/metals/src/main/scala/scala/meta/internal/metals/codeactions/InsertInferredType.scala b/metals/src/main/scala/scala/meta/internal/metals/codeactions/InsertInferredType.scala index b48cb37b498..36592d71eb4 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codeactions/InsertInferredType.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codeactions/InsertInferredType.scala @@ -33,6 +33,10 @@ class InsertInferredType( import InsertInferredType._ override def kind: String = l.CodeActionKind.QuickFix + override val maybeCodeActionId: Option[String] = Some( + "InsertInferredType" + ) + override def handleCommand( textDocumentParams: l.TextDocumentPositionParams, token: CancelToken, diff --git a/mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompiler.java b/mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompiler.java index 62aa8a3a787..23fc875fae8 100644 --- a/mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompiler.java +++ b/mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompiler.java @@ -13,6 +13,7 @@ import java.net.URI; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collections; import java.util.concurrent.CompletableFuture; import java.util.List; @@ -111,6 +112,27 @@ public CompletableFuture> references(References return CompletableFuture.completedFuture(Collections.emptyList()); } + /** + * Execute the given code action + */ + public CompletableFuture> codeAction(OffsetParams params, String codeActionId, Object codeActionPayload) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + /** + * Returns the list of code actions supported by the current presentation compiler. + */ + public List supportedCodeActions() { + return Arrays.asList( + "ConvertToNamedArguments", + "ExtractMethod", + "ImplementAbstractMembers", + "ImportMissingSymbol", + "InlineValue", + "InsertInferredType" + ); + } + /** * Return decoded and pretty printed TASTy content for .scala or .tasty file. * diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala index 78d668ec4b8..41ecb44f786 100644 --- a/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala @@ -363,6 +363,7 @@ case class ScalaPresentationCompiler( case Right(edits: List[l.TextEdit]) => edits.asJava } end convertToNamedArguments + override def selectionRange( params: ju.List[OffsetParams] ): CompletableFuture[ju.List[l.SelectionRange]] =