-
Notifications
You must be signed in to change notification settings - Fork 333
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement 'convert to named lambda parameters' code action
- Loading branch information
1 parent
057e7bc
commit 601c8f7
Showing
14 changed files
with
572 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
...rc/main/scala/scala/meta/internal/metals/codeactions/ConvertToNamedLambdaParameters.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package scala.meta.internal.metals.codeactions | ||
|
||
import scala.concurrent.ExecutionContext | ||
import scala.concurrent.Future | ||
|
||
import scala.meta.Term | ||
import scala.meta.internal.metals.Compilers | ||
import scala.meta.internal.metals.MetalsEnrichments._ | ||
import scala.meta.internal.metals.ServerCommands | ||
import scala.meta.internal.metals.clients.language.MetalsLanguageClient | ||
import scala.meta.internal.metals.codeactions.CodeAction | ||
import scala.meta.internal.metals.codeactions.CodeActionBuilder | ||
import scala.meta.internal.metals.logging | ||
import scala.meta.internal.parsing.Trees | ||
import scala.meta.pc.CancelToken | ||
|
||
import org.eclipse.{lsp4j => l} | ||
|
||
/** | ||
* Code action to convert a wildcard lambda to a lambda with named parameters | ||
* e.g. | ||
* | ||
* List(1, 2).map(<<_>> + 1) => List(1, 2).map(i => i + 1) | ||
*/ | ||
class ConvertToNamedLambdaParameters( | ||
trees: Trees, | ||
compilers: Compilers, | ||
languageClient: MetalsLanguageClient, | ||
) extends CodeAction { | ||
|
||
override val kind: String = l.CodeActionKind.RefactorRewrite | ||
|
||
override type CommandData = | ||
ServerCommands.ConvertToNamedLambdaParametersRequest | ||
|
||
override def command: Option[ActionCommand] = Some( | ||
ServerCommands.ConvertToNamedLambdaParameters | ||
) | ||
|
||
override def handleCommand( | ||
data: ServerCommands.ConvertToNamedLambdaParametersRequest, | ||
token: CancelToken, | ||
)(implicit ec: ExecutionContext): Future[Unit] = { | ||
val uri = data.position.getTextDocument().getUri() | ||
for { | ||
edits <- compilers.convertToNamedLambdaParameters( | ||
data.position, | ||
token, | ||
) | ||
_ = logging.logErrorWhen( | ||
edits.isEmpty(), | ||
s"Could not convert lambda at position ${data.position} to named lambda", | ||
) | ||
workspaceEdit = new l.WorkspaceEdit(Map(uri -> edits).asJava) | ||
_ <- languageClient | ||
.applyEdit(new l.ApplyWorkspaceEditParams(workspaceEdit)) | ||
.asScala | ||
} yield () | ||
} | ||
|
||
override def contribute( | ||
params: l.CodeActionParams, | ||
token: CancelToken, | ||
)(implicit ec: ExecutionContext): Future[Seq[l.CodeAction]] = { | ||
val path = params.getTextDocument().getUri().toAbsolutePath | ||
val range = params.getRange() | ||
val maybeLambda = | ||
trees.findLastEnclosingAt[Term.AnonymousFunction](path, range.getStart()) | ||
maybeLambda | ||
.map { lambda => | ||
val position = new l.TextDocumentPositionParams( | ||
params.getTextDocument(), | ||
new l.Position(lambda.pos.startLine, lambda.pos.startColumn), | ||
) | ||
val command = | ||
ServerCommands.ConvertToNamedLambdaParameters.toLsp( | ||
ServerCommands.ConvertToNamedLambdaParametersRequest(position) | ||
) | ||
val codeAction = CodeActionBuilder.build( | ||
title = ConvertToNamedLambdaParameters.title, | ||
kind = kind, | ||
command = Some(command), | ||
) | ||
Future.successful(Seq(codeAction)) | ||
} | ||
.getOrElse(Future.successful(Nil)) | ||
} | ||
|
||
} | ||
|
||
object ConvertToNamedLambdaParameters { | ||
def title: String = "Convert to named lambda parameters" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
mtags/src/main/scala-2/scala/meta/internal/pc/ConvertToNamedLambdaParametersProvider.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package scala.meta.internal.pc | ||
|
||
import scala.meta.pc.OffsetParams | ||
|
||
import org.eclipse.{lsp4j => l} | ||
|
||
final class ConvertToNamedLambdaParametersProvider( | ||
val compiler: MetalsGlobal, | ||
offsetParam: OffsetParams | ||
) { | ||
|
||
def convertToNamedLambdaParameters: Either[String, List[l.TextEdit]] = { | ||
Right(Nil) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
mtags/src/main/scala-3/scala/meta/internal/mtags/TermNameInference.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package scala.meta.internal.mtags | ||
|
||
/** | ||
* Helpers for generating variable names based on the desired types. | ||
*/ | ||
object TermNameInference { | ||
|
||
/** Single character names for types. (`Int` => `i`, `i1`, `i2`, ...) */ | ||
def singleLetterNameStream(typeName: String): LazyList[String] = { | ||
val typeName1 = sanitizeInput(typeName) | ||
val firstCharStr = typeName1.headOption.getOrElse('x').toLower.toString | ||
numberedStreamFromName(firstCharStr) | ||
} | ||
|
||
/** Names only from upper case letters (`OnDemandSymbolIndex` => `odsi`, `odsi1`, `odsi2`, ...) */ | ||
def shortNameStream(typeName: String): LazyList[String] = { | ||
val typeName1 = sanitizeInput(typeName) | ||
val upperCases = typeName1.filter(_.isUpper).map(_.toLower) | ||
val name = if (upperCases.isEmpty) typeName1 else upperCases | ||
numberedStreamFromName(name) | ||
} | ||
|
||
/** Names from lower case letters (`OnDemandSymbolIndex` => `onDemandSymbolIndex`, `onDemandSymbolIndex1`, ...) */ | ||
def fullNameStream(typeName: String): LazyList[String] = { | ||
val typeName1 = sanitizeInput(typeName) | ||
val withFirstLower = | ||
typeName1.headOption.map(_.toLower).getOrElse('x') + typeName1.drop(1) | ||
numberedStreamFromName(withFirstLower) | ||
} | ||
|
||
/** A lazy list of names: a, b, ..., z, aa, ab, ..., az, ba, bb, ... */ | ||
def saneNamesStream: LazyList[String] = { | ||
val letters = ('a' to 'z').map(_.toString) | ||
def computeNext(acc: String): String = { | ||
if (acc.last == 'z') | ||
computeNext(acc.init) + letters.head | ||
else | ||
acc.init + letters(letters.indexOf(acc.last) + 1) | ||
} | ||
def loop(acc: String): LazyList[String] = | ||
acc #:: loop(computeNext(acc)) | ||
loop("a") | ||
} | ||
|
||
private def sanitizeInput(typeName: String): String = | ||
typeName.filter(_.isLetterOrDigit) | ||
|
||
private def numberedStreamFromName(name: String): LazyList[String] = { | ||
val rest = LazyList.from(1).map(name + _) | ||
name #:: rest | ||
} | ||
} |
Oops, something went wrong.