Skip to content

Backport "Backport from Metals" to 3.3 LTS #211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ object CompletionItemResolver extends ItemResolver:
else gsymDoc
else
val companionDoc = docs(companion)
if companionDoc.isEmpty() then gsymDoc
if companionDoc.isEmpty() || companionDoc == gsymDoc then gsymDoc
else if gsymDoc.isEmpty() then companionDoc
else
List(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,15 @@ class PcInlayHintsProvider(
LabelPart(")") :: Nil,
InlayHintKind.Parameter,
)
case ImplicitParameters(symbols, pos, allImplicit) =>
val labelParts = symbols.map(s => List(labelPart(s, s.decodedName)))
val label =
if allImplicit then labelParts.separated("(using ", ", ", ")")
else labelParts.separated(", ")
case ImplicitParameters(trees, pos) =>
inlayHints.add(
adjustPos(pos).toLsp,
label,
InlayHintKind.Parameter,
ImplicitParameters.partsFromImplicitArgs(trees).map((label, maybeSymbol) =>
maybeSymbol match
case Some(symbol) => labelPart(symbol, label)
case None => LabelPart(label)
),
InlayHintKind.Parameter
)
case ValueOf(label, pos) =>
inlayHints.add(
Expand Down Expand Up @@ -221,12 +221,8 @@ object ImplicitParameters:
case Apply(fun, args)
if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent && !args.exists(isQuotes(_)) =>
val (implicitArgs, providedArgs) = args.partition(isSyntheticArg)
val allImplicit = providedArgs.isEmpty || providedArgs.forall {
case Ident(name) => name == nme.MISSING
case _ => false
}
val pos = implicitArgs.head.sourcePos
Some(implicitArgs.map(_.symbol), pos, allImplicit)
Some(implicitArgs, pos)
case _ => None
} else None

Expand All @@ -242,6 +238,67 @@ object ImplicitParameters:
private def isQuotes(tree: Tree)(using Context) =
tree.tpe.typeSymbol == defn.QuotesClass

def partsFromImplicitArgs(trees: List[Tree])(using Context): List[(String, Option[Symbol])] = {
@tailrec
def recurseImplicitArgs(
currentArgs: List[Tree],
remainingArgsLists: List[List[Tree]],
parts: List[(String, Option[Symbol])]
): List[(String, Option[Symbol])] =
(currentArgs, remainingArgsLists) match {
case (Nil, Nil) => parts
case (Nil, headArgsList :: tailArgsList) =>
if (headArgsList.isEmpty) {
recurseImplicitArgs(
headArgsList,
tailArgsList,
(")", None) :: parts
)
} else {
recurseImplicitArgs(
headArgsList,
tailArgsList,
(", ", None) :: (")", None) :: parts
)
}
case (arg :: remainingArgs, remainingArgsLists) =>
arg match {
case Apply(fun, args) =>
val applyLabel = (fun.symbol.decodedName, Some(fun.symbol))
recurseImplicitArgs(
args,
remainingArgs :: remainingArgsLists,
("(", None) :: applyLabel :: parts
)
case t if t.isTerm =>
val termLabel = (t.symbol.decodedName, Some(t.symbol))
if (remainingArgs.isEmpty)
recurseImplicitArgs(
remainingArgs,
remainingArgsLists,
termLabel :: parts
)
else
recurseImplicitArgs(
remainingArgs,
remainingArgsLists,
(", ", None) :: termLabel :: parts
)
case _ =>
recurseImplicitArgs(
remainingArgs,
remainingArgsLists,
parts
)
}
}
((")", None) :: recurseImplicitArgs(
trees,
Nil,
List(("(using ", None))
)).reverse
}

end ImplicitParameters

object ValueOf:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import dotty.tools.dotc.interactive.Interactive
import dotty.tools.dotc.interactive.InteractiveDriver
import dotty.tools.dotc.util.SourcePosition
import dotty.tools.pc.utils.InteractiveEnrichments.*
import dotty.tools.pc.IndexedContext.Result

import org.eclipse.lsp4j as l

Expand Down Expand Up @@ -49,7 +50,9 @@ final class PcInlineValueProviderImpl(
DefinitionTree(defn, pos)
}
.toRight(Errors.didNotFindDefinition)
symbols = symbolsUsedInDefn(definition.tree.rhs)
path = Interactive.pathTo(unit.tpdTree, definition.tree.rhs.span)(using newctx)
indexedContext = IndexedContext(Interactive.contextOfPath(path)(using newctx))
symbols = symbolsUsedInDefn(definition.tree.rhs).filter(indexedContext.lookupSym(_) == Result.InScope)
references <- getReferencesToInline(definition, allOccurences, symbols)
yield
val (deleteDefinition, refsEdits) = references
Expand Down Expand Up @@ -111,27 +114,25 @@ final class PcInlineValueProviderImpl(
val adjustedEnd = extend(pos.end - 1, ')', 1) + 1
text.slice(adjustedStart, adjustedEnd).mkString

private def symbolsUsedInDefn(
rhs: Tree
): List[Symbol] =
private def symbolsUsedInDefn(rhs: Tree): Set[Symbol] =
def collectNames(
symbols: List[Symbol],
symbols: Set[Symbol],
tree: Tree
): List[Symbol] =
): Set[Symbol] =
tree match
case id: (Ident | Select)
if !id.symbol.is(Synthetic) && !id.symbol.is(Implicit) =>
tree.symbol :: symbols
symbols + tree.symbol
case _ => symbols

val traverser = new DeepFolder[List[Symbol]](collectNames)
traverser(List(), rhs)
val traverser = new DeepFolder[Set[Symbol]](collectNames)
traverser(Set(), rhs)
end symbolsUsedInDefn

private def getReferencesToInline(
definition: DefinitionTree,
allOccurences: List[Occurence],
symbols: List[Symbol]
symbols: Set[Symbol]
): Either[String, (Boolean, List[Reference])] =
val defIsLocal = definition.tree.symbol.ownersIterator
.drop(1)
Expand All @@ -156,7 +157,7 @@ final class PcInlineValueProviderImpl(

private def makeRefsEdits(
refs: List[Occurence],
symbols: List[Symbol]
symbols: Set[Symbol]
): Either[String, List[Reference]] =
val newctx = driver.currentCtx.fresh.setCompilationUnit(unit)
def buildRef(occurrence: Occurence): Either[String, Reference] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ case class ScalaPresentationCompiler(
completionItemPriority: CompletionItemPriority = (_: String) => 0,
) extends PresentationCompiler:

override def supportedCodeActions(): ju.List[String] = List(
CodeActionId.ConvertToNamedArguments,
CodeActionId.ImplementAbstractMembers,
CodeActionId.ExtractMethod,
CodeActionId.InlineValue,
CodeActionId.InsertInferredType
).asJava

def this() = this("", None, Nil, Nil)

val scalaVersion = BuildInfo.scalaVersion
Expand All @@ -67,6 +75,38 @@ case class ScalaPresentationCompiler(
.map(StdReportContext(_, _ => buildTargetName, reportsLevel))
.getOrElse(EmptyReportContext)

override def codeAction[T](
params: OffsetParams,
codeActionId: String,
codeActionPayload: Optional[T]
): CompletableFuture[ju.List[TextEdit]] =
(codeActionId, codeActionPayload.asScala) match
case (
CodeActionId.ConvertToNamedArguments,
Some(argIndices: ju.List[_])
) =>
val payload =
argIndices.asScala.collect { case i: Integer => i.toInt }.toSet
convertToNamedArguments(params, payload)
case (CodeActionId.ImplementAbstractMembers, _) =>
implementAbstractMembers(params)
case (CodeActionId.InsertInferredType, _) =>
insertInferredType(params)
case (CodeActionId.InlineValue, _) =>
inlineValue(params)
case (CodeActionId.ExtractMethod, Some(extractionPos: OffsetParams)) =>
params match {
case range: RangeParams =>
extractMethod(range, extractionPos)
case _ => failedFuture(new IllegalArgumentException(s"Expected range parameters"))
}
case (id, _) => failedFuture(new IllegalArgumentException(s"Unsupported action id $id"))

private def failedFuture[T](e: Throwable): CompletableFuture[T] =
val f = new CompletableFuture[T]()
f.completeExceptionally(e)
f

override def withCompletionItemPriority(
priority: CompletionItemPriority
): PresentationCompiler =
Expand Down Expand Up @@ -348,14 +388,20 @@ case class ScalaPresentationCompiler(
override def convertToNamedArguments(
params: OffsetParams,
argIndices: ju.List[Integer]
): CompletableFuture[ju.List[l.TextEdit]] =
convertToNamedArguments(params, argIndices.asScala.toSet.map(_.toInt))

def convertToNamedArguments(
params: OffsetParams,
argIndices: Set[Int]
): CompletableFuture[ju.List[l.TextEdit]] =
val empty: Either[String, List[l.TextEdit]] = Right(List())
(compilerAccess
.withNonInterruptableCompiler(Some(params))(empty, params.token()) { pc =>
new ConvertToNamedArgumentsProvider(
pc.compiler(),
params,
argIndices.asScala.map(_.toInt).toSet
argIndices
).convertToNamedArguments
})
.thenApplyAsync {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ class CompletionProvider(
// related issue https://github.com/lampepfl/scala3/issues/11941
lazy val kind: CompletionItemKind = underlyingCompletion.completionItemKind
val description = underlyingCompletion.description(printer)
val label = underlyingCompletion.labelWithDescription(printer)
val label =
if config.isDetailIncludedInLabel then completion.labelWithDescription(printer)
else completion.label
val ident = underlyingCompletion.insertText.getOrElse(underlyingCompletion.label)

lazy val isInStringInterpolation =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package dotty.tools.pc.utils

import java.util.Optional

import scala.annotation.tailrec
import scala.meta.internal.jdk.CollectionConverters.*
import scala.meta.internal.mtags.CommonMtagsEnrichments
Expand Down Expand Up @@ -272,11 +274,14 @@ object InteractiveEnrichments extends CommonMtagsEnrichments:
symbol.maybeOwner.companion,
).filter(_ != NoSymbol) ++ symbol.allOverriddenSymbols
else symbol.allOverriddenSymbols
val documentation = search.documentation(
sym,
() => parentSymbols.iterator.map(toSemanticdbSymbol).toList.asJava,
contentType,
)
val documentation =
if symbol.isLocal then Optional.empty
else
search.documentation(
sym,
() => parentSymbols.iterator.map(toSemanticdbSymbol).toList.asJava,
contentType,
)
documentation.nn.toScala
end symbolDocumentation
end extension
Expand Down
Loading