Skip to content

Commit

Permalink
chore: Backport changes for presentation compiler (#20345)
Browse files Browse the repository at this point in the history
  • Loading branch information
tgodzik authored and WojciechMazur committed Jul 6, 2024
1 parent ab64751 commit 582eb46
Show file tree
Hide file tree
Showing 11 changed files with 409 additions and 93 deletions.
19 changes: 12 additions & 7 deletions presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.util as ju
import scala.meta.internal.metals.Report
import scala.meta.internal.metals.ReportContext
import scala.meta.internal.pc.ScalaHover
import scala.meta.pc.ContentType
import scala.meta.pc.HoverSignature
import scala.meta.pc.OffsetParams
import scala.meta.pc.SymbolSearch
Expand All @@ -30,7 +31,8 @@ object HoverProvider:
def hover(
params: OffsetParams,
driver: InteractiveDriver,
search: SymbolSearch
search: SymbolSearch,
contentType: ContentType
)(implicit reportContext: ReportContext): ju.Optional[HoverSignature] =
val uri = params.uri().nn
val text = params.text().nn
Expand Down Expand Up @@ -101,10 +103,10 @@ object HoverProvider:
skipCheckOnName
) match
case Nil =>
fallbackToDynamics(path, printer)
fallbackToDynamics(path, printer, contentType)
case (symbol, tpe) :: _
if symbol.name == nme.selectDynamic || symbol.name == nme.applyDynamic =>
fallbackToDynamics(path, printer)
fallbackToDynamics(path, printer, contentType)
case symbolTpes @ ((symbol, tpe) :: _) =>
val exprTpw = tpe.widenTermRefExpr.deepDealias
val hoverString =
Expand All @@ -126,7 +128,7 @@ object HoverProvider:
end hoverString

val docString = symbolTpes
.flatMap(symTpe => search.symbolDocumentation(symTpe._1))
.flatMap(symTpe => search.symbolDocumentation(symTpe._1, contentType))
.map(_.docstring())
.mkString("\n")
printer.expressionType(exprTpw) match
Expand All @@ -144,7 +146,8 @@ object HoverProvider:
symbolSignature = Some(hoverString),
docstring = Some(docString),
forceExpressionType = forceExpressionType,
contextInfo = printer.getUsedRenamesInfo
contextInfo = printer.getUsedRenamesInfo,
contentType = contentType
)
).nn
case _ =>
Expand All @@ -159,7 +162,8 @@ object HoverProvider:

private def fallbackToDynamics(
path: List[Tree],
printer: ShortenedTypePrinter
printer: ShortenedTypePrinter,
contentType: ContentType
)(using Context): ju.Optional[HoverSignature] = path match
case SelectDynamicExtractor(sel, n, name) =>
def findRefinement(tp: Type): Option[HoverSignature] =
Expand All @@ -178,7 +182,8 @@ object HoverProvider:
new ScalaHover(
expressionType = Some(tpeString),
symbolSignature = Some(s"$valOrDef $name$tpeString"),
contextInfo = printer.getUsedRenamesInfo
contextInfo = printer.getUsedRenamesInfo,
contentType = contentType
)
)
case RefinedType(parent, _, _) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class PcInlayHintsProvider(
val source =
SourceFile.virtual(filePath.toString, sourceText)
driver.run(uri, source)
given InlayHintsParams = params

given InferredType.Text = InferredType.Text(text)
given ctx: Context = driver.currentCtx
Expand All @@ -65,7 +66,7 @@ class PcInlayHintsProvider(
tree: Tree,
): InlayHints =
tree match
case ImplicitConversion(symbol, range) if params.implicitConversions() =>
case ImplicitConversion(symbol, range) =>
val adjusted = adjustPos(range)
inlayHints
.add(
Expand All @@ -78,8 +79,7 @@ class PcInlayHintsProvider(
LabelPart(")") :: Nil,
InlayHintKind.Parameter,
)
case ImplicitParameters(symbols, pos, allImplicit)
if params.implicitParameters() =>
case ImplicitParameters(symbols, pos, allImplicit) =>
val labelParts = symbols.map(s => List(labelPart(s, s.decodedName)))
val label =
if allImplicit then labelParts.separated("(using ", ", ", ")")
Expand All @@ -89,22 +89,22 @@ class PcInlayHintsProvider(
label,
InlayHintKind.Parameter,
)
case ValueOf(label, pos) if params.implicitParameters() =>
case ValueOf(label, pos) =>
inlayHints.add(
adjustPos(pos).toLsp,
LabelPart("(") :: LabelPart(label) :: List(LabelPart(")")),
InlayHintKind.Parameter,
)
case TypeParameters(tpes, pos, sel)
if params.typeParameters() && !syntheticTupleApply(sel) =>
if !syntheticTupleApply(sel) =>
val label = tpes.map(toLabelParts(_, pos)).separated("[", ", ", "]")
inlayHints.add(
adjustPos(pos).endPos.toLsp,
label,
InlayHintKind.Type,
)
case InferredType(tpe, pos, defTree)
if params.inferredTypes() && !isErrorTpe(tpe) =>
if !isErrorTpe(tpe) =>
val adjustedPos = adjustPos(pos).endPos
if inlayHints.containsDef(adjustedPos.start) then inlayHints
else
Expand Down Expand Up @@ -191,14 +191,16 @@ class PcInlayHintsProvider(
end PcInlayHintsProvider

object ImplicitConversion:
def unapply(tree: Tree)(using Context) =
tree match
case Apply(fun: Ident, args) if isSynthetic(fun) =>
implicitConversion(fun, args)
case Apply(Select(fun, name), args)
if name == nme.apply && isSynthetic(fun) =>
implicitConversion(fun, args)
case _ => None
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) =
if (params.implicitConversions()) {
tree match
case Apply(fun: Ident, args) if isSynthetic(fun) =>
implicitConversion(fun, args)
case Apply(Select(fun, name), args)
if name == nme.apply && isSynthetic(fun) =>
implicitConversion(fun, args)
case _ => None
} else None
private def isSynthetic(tree: Tree)(using Context) =
tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit)

Expand All @@ -212,52 +214,64 @@ object ImplicitConversion:
end ImplicitConversion

object ImplicitParameters:
def unapply(tree: Tree)(using Context) =
tree match
case Apply(fun, args)
if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent =>
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)
case _ => None
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) =
if (params.implicitParameters()) {
tree match
case Apply(fun, args)
if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent =>
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)
case _ => None
} else None

private def isSyntheticArg(tree: Tree)(using Context) = tree match
case tree: Ident =>
tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit)
tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) &&
!isQuotes(tree)
case _ => false

// Decorations for Quotes are rarely useful
private def isQuotes(tree: Tree)(using Context) =
tree.tpe.typeSymbol == defn.QuotesClass

end ImplicitParameters

object ValueOf:
def unapply(tree: Tree)(using Context) =
tree match
case Apply(ta @ TypeApply(fun, _), _)
if fun.span.isSynthetic && isValueOf(fun) =>
Some(
"new " + tpnme.valueOf.decoded.capitalize + "(...)",
fun.sourcePos,
)
case _ => None
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) =
if (params.implicitParameters()) {
tree match
case Apply(ta @ TypeApply(fun, _), _)
if fun.span.isSynthetic && isValueOf(fun) =>
Some(
"new " + tpnme.valueOf.decoded.capitalize + "(...)",
fun.sourcePos,
)
case _ => None
} else None
private def isValueOf(tree: Tree)(using Context) =
val symbol = tree.symbol.maybeOwner
symbol.name.decoded == tpnme.valueOf.decoded.capitalize
end ValueOf

object TypeParameters:
def unapply(tree: Tree)(using Context) =
tree match
case TypeApply(sel: Select, _) if sel.isForComprehensionMethod => None
case TypeApply(fun, args) if inferredTypeArgs(args) =>
val pos = fun match
case sel: Select if sel.isInfix =>
sel.sourcePos.withEnd(sel.nameSpan.end)
case _ => fun.sourcePos
val tpes = args.map(_.typeOpt.stripTypeVar.widen.finalResultType)
Some((tpes, pos.endPos, fun))
case _ => None
def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) =
if (params.typeParameters()) {
tree match
case TypeApply(sel: Select, _)
if sel.isForComprehensionMethod || sel.isInfix ||
sel.symbol.name == nme.unapply =>
None
case TypeApply(fun, args) if inferredTypeArgs(args) =>
val tpes = args.map(_.tpe.stripTypeVar.widen.finalResultType)
Some((tpes, fun.sourcePos.endPos, fun))
case _ => None
} else None

private def inferredTypeArgs(args: List[Tree]): Boolean =
args.forall {
case tt: TypeTree if tt.span.exists && !tt.span.isZeroExtent => true
Expand All @@ -270,29 +284,35 @@ object InferredType:
object Text:
def apply(text: Array[Char]): Text = text

def unapply(tree: Tree)(using text: Text, cxt: Context) =
tree match
case vd @ ValDef(_, tpe, _)
if isValidSpan(tpe.span, vd.nameSpan) &&
!vd.symbol.is(Flags.Enum) &&
!isValDefBind(text, vd) =>
if vd.symbol == vd.symbol.sourceSymbol then
Some(tpe.typeOpt, tpe.sourcePos.withSpan(vd.nameSpan), vd)
else None
case vd @ DefDef(_, _, tpe, _)
if isValidSpan(tpe.span, vd.nameSpan) &&
tpe.span.start >= vd.nameSpan.end &&
!vd.symbol.isConstructor &&
!vd.symbol.is(Flags.Mutable) =>
if vd.symbol == vd.symbol.sourceSymbol then
Some(tpe.typeOpt, tpe.sourcePos, vd)
else None
case bd @ Bind(
name,
Ident(nme.WILDCARD),
) =>
Some(bd.symbol.info, bd.namePos, bd)
case _ => None
def unapply(tree: Tree)(using params: InlayHintsParams, text: Text, ctx: Context) =
if (params.inferredTypes()) {
tree match
case vd @ ValDef(_, tpe, _)
if isValidSpan(tpe.span, vd.nameSpan) &&
!vd.symbol.is(Flags.Enum) &&
(isNotInUnapply(vd) || params.hintsInPatternMatch()) &&
!isValDefBind(text, vd) =>
if vd.symbol == vd.symbol.sourceSymbol then
Some(tpe.tpe, tpe.sourcePos.withSpan(vd.nameSpan), vd)
else None
case vd @ DefDef(_, _, tpe, _)
if isValidSpan(tpe.span, vd.nameSpan) &&
tpe.span.start >= vd.nameSpan.end &&
!vd.symbol.isConstructor &&
!vd.symbol.is(Flags.Mutable) =>
if vd.symbol == vd.symbol.sourceSymbol then
Some(tpe.tpe, tpe.sourcePos, vd)
else None
case bd @ Bind(
name,
Ident(nme.WILDCARD),
) if !bd.span.isZeroExtent && bd.symbol.isTerm && params.hintsInPatternMatch() =>
Some(bd.symbol.info, bd.namePos, bd)
case _ => None
} else None

private def isNotInUnapply(vd: ValDef)(using Context) =
vd.rhs.span.exists && vd.rhs.span.start > vd.nameSpan.end

private def isValidSpan(tpeSpan: Span, nameSpan: Span): Boolean =
tpeSpan.isZeroExtent &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ case class ScalaPresentationCompiler(
params.token()
) { access =>
val driver = access.compiler()
HoverProvider.hover(params, driver, search)
HoverProvider.hover(params, driver, search, config.hoverContentType())
}
end hover

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import dotty.tools.dotc.core.Symbols.NoSymbol
import dotty.tools.dotc.core.Symbols.Symbol
import dotty.tools.dotc.core.Types.AndType
import dotty.tools.dotc.core.Types.ClassInfo
import dotty.tools.dotc.core.Types.NoType
import dotty.tools.dotc.core.Types.OrType
import dotty.tools.dotc.core.Types.Type
import dotty.tools.dotc.core.Types.TypeRef
Expand Down Expand Up @@ -94,7 +95,7 @@ object CaseKeywordCompletion:
Some(sel.tpe.widen.deepDealias)

selTpe
.map { selTpe =>
.collect { case selTpe if selTpe != NoType =>
val selectorSym = selTpe.typeSymbol
// Special handle case when selector is a tuple or `FunctionN`.
if definitions.isTupleClass(selectorSym) || definitions.isFunctionClass(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import scala.annotation.tailrec
import scala.meta.internal.jdk.CollectionConverters.*
import scala.meta.internal.mtags.CommonMtagsEnrichments
import scala.meta.internal.mtags.KeywordWrapper
import scala.meta.pc.ContentType
import scala.meta.pc.OffsetParams
import scala.meta.pc.RangeParams
import scala.meta.pc.SymbolDocumentation
Expand Down Expand Up @@ -260,7 +261,7 @@ object InteractiveEnrichments extends CommonMtagsEnrichments:
}

extension (search: SymbolSearch)
def symbolDocumentation(symbol: Symbol)(using
def symbolDocumentation(symbol: Symbol, contentType: ContentType = ContentType.MARKDOWN)(using
Context
): Option[SymbolDocumentation] =
def toSemanticdbSymbol(symbol: Symbol) =
Expand All @@ -280,6 +281,7 @@ object InteractiveEnrichments extends CommonMtagsEnrichments:
val documentation = search.documentation(
sym,
() => parentSymbols.iterator.map(toSemanticdbSymbol).toList.asJava,
contentType,
)
documentation.nn.toScala
end symbolDocumentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class BaseInlayHintsSuite extends BasePCSuite {
base: String,
expected: String,
kind: Option[Int] = None,
hintsInPatternMatch: Boolean = false
): Unit =
def pkgWrap(text: String) =
if (text.contains("package")) text
Expand All @@ -35,7 +36,8 @@ class BaseInlayHintsSuite extends BasePCSuite {
true,
true,
true,
true
true,
hintsInPatternMatch
)

val inlayHints = presentationCompiler
Expand All @@ -49,8 +51,8 @@ class BaseInlayHintsSuite extends BasePCSuite {
val obtained = TestInlayHints.applyInlayHints(withPkg, inlayHints)

assertNoDiff(
pkgWrap(expected),
obtained,
pkgWrap(expected)
)

}
Loading

0 comments on commit 582eb46

Please sign in to comment.