Skip to content

SCL-23855: show type parameters in XRay mode #698

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

Open
wants to merge 1 commit into
base: idea251.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,25 @@ import org.jetbrains.plugins.scala.codeInsight.ScalaCodeInsightBundle
import org.jetbrains.plugins.scala.codeInsight.hints.ScalaTypeHintsPass._
import org.jetbrains.plugins.scala.extensions._
import org.jetbrains.plugins.scala.lang.lexer.ScalaTokenTypes
import org.jetbrains.plugins.scala.lang.psi.api.base.ScPatternList
import org.jetbrains.plugins.scala.lang.psi.api.InferUtil
import org.jetbrains.plugins.scala.lang.psi.api.base.{ScConstructorInvocation, ScMethodLike, ScPatternList, ScPrimaryConstructor}
import org.jetbrains.plugins.scala.lang.psi.api.base.patterns.{ScReferencePattern, ScTypedPatternLike, ScWildcardPattern}
import org.jetbrains.plugins.scala.lang.psi.api.expr.{ScExpression, ScInfixExpr, ScReferenceExpression, ScTypedExpression, ScUnderscoreSection}
import org.jetbrains.plugins.scala.lang.psi.api.statements.params.{ScParameter, ScParameterClause}
import org.jetbrains.plugins.scala.lang.psi.api.expr.{ScExpression, ScInfixExpr, ScMethodCall, ScReferenceExpression, ScTypedExpression, ScUnderscoreSection}
import org.jetbrains.plugins.scala.lang.psi.api.statements.params.{ScParameter, ScParameterClause, ScTypeParam}
import org.jetbrains.plugins.scala.lang.psi.api.statements.{ScFunction, ScFunctionDefinition, ScPatternDefinition, ScValueOrVariable, ScVariableDefinition}
import org.jetbrains.plugins.scala.lang.psi.types.nonvalue.ScMethodType
import org.jetbrains.plugins.scala.lang.psi.api.toplevel.typedef.ScTypeDefinition
import org.jetbrains.plugins.scala.lang.psi.types.api.TypeParameter
import org.jetbrains.plugins.scala.lang.psi.types.nonvalue.{ScMethodType, ScTypePolymorphicType}
import org.jetbrains.plugins.scala.lang.psi.types.result.Typeable
import org.jetbrains.plugins.scala.lang.psi.types.{ScType, TypePresentationContext}
import org.jetbrains.plugins.scala.lang.psi.types.{ConstraintSystem, ScAbstractType, ScType, TypePresentationContext}
import org.jetbrains.plugins.scala.lang.resolve.ScalaResolveResult
import org.jetbrains.plugins.scala.settings.ScalaApplicationSettings.{getInstance => ScalaApplicationSettings}
import org.jetbrains.plugins.scala.settings.ScalaHighlightingMode
import org.jetbrains.plugins.scala.settings.annotations.Definition
import org.jetbrains.plugins.scala.settings.annotations.Definition.{FunctionDefinition, ValueDefinition, VariableDefinition}

import scala.annotation.tailrec

private[codeInsight] trait ScalaTypeHintsPass {
protected implicit def settings: ScalaHintsSettings

Expand All @@ -54,10 +60,51 @@ private[codeInsight] trait ScalaTypeHintsPass {
}

private def collectXRayHints(editor: Editor, root: PsiElement) = root.elements(_.isVisible).flatMap {
case [email protected](Resolved(r@ScalaResolveResult(fun: ScMethodLike, _)))
if getTypeParameters(fun).nonEmpty && ScalaApplicationSettings.XRAY_SHOW_TYPE_PARAMETERS_HINTS =>
r.resultUndef.flatMap { cs =>
xRayTypeParametersHints(ci.typeElement, cs, fun, editor)
}.getOrElse(Seq.empty)
case [email protected]((invoked: ScReferenceExpression) & Resolved(ScalaResolveResult(methodLike: ScMethodLike, _)))
if ScalaApplicationSettings.XRAY_SHOW_TYPE_PARAMETERS_HINTS && !outermostMethodCall.getParent.is[ScMethodCall] && getTypeParameters(methodLike).nonEmpty =>
val cs = collectMethodCalls(outermostMethodCall)
.map { mc =>
for {
typePoly <- mc.getNonValueType(fromUnderscore = true).toOption.flatMap(_.asOptionOf[ScTypePolymorphicType])
inferRes = InferUtil.localTypeInferenceWithApplicabilityExt(
typePoly.internalType,
mc.matchedParameters.map(_._2),
mc.matchedParameters.map(_._1),
typePoly.typeParameters
)
} yield inferRes._2.constraints
}
.collect { case Some(x) => x }
.foldLeft(ConstraintSystem.empty)(_ + _)
val hints = xRayTypeParametersHints(invoked, cs, methodLike, editor)
hints.getOrElse(Seq.empty)
case e @ Typeable(t) => xRayHintsFor(e, t)(editor.getColorsScheme, TypePresentationContext(e), settings)
case _ => Seq.empty
}

private def xRayTypeParametersHints(invoked: PsiElement, cs: ConstraintSystem, fun: ScMethodLike, editor: Editor) = {
cs.substitutionBounds(canThrowSCE = false)(editor.getProject).map { bounds =>
def typeParamSubst(tp: ScTypeParam) = {
bounds.substitutor(ScAbstractType(TypeParameter(tp), tp.lowerBound.getOrNothing, tp.upperBound.getOrAny))
}

getTypeParameters(fun).map { tp =>
val ty = typeParamSubst(tp)
textPartsOf(ty, settings.presentationLength, invoked)(editor.getColorsScheme, TypePresentationContext(invoked))
}
}.map(texts =>
Seq(
Hint(Seq(Text("[")), invoked, suffix = true, corners = Corners.Left),
Hint(texts.intersperse(Seq(Text(", "))).flatten :+ Text("]"), invoked, suffix = true, relatesToPrecedingElement = true, corners = Corners.Right)
)
)
}

private def xRayHintsFor(e: PsiElement, t: ScType)(implicit scheme: EditorColorsScheme, context: TypePresentationContext, settings: ScalaHintsSettings): Seq[Hint] = e match {
case e: ScParameter if e.typeElement.isEmpty && ScalaApplicationSettings.XRAY_SHOW_LAMBDA_PARAMETER_HINTS =>
hints(e, t, inParentheses = e.getParent.is[ScParameterClause] && e.getParent.getFirstChild.elementType != ScalaTokenTypes.tLPARENTHESIS)
Expand All @@ -74,6 +121,27 @@ private[codeInsight] trait ScalaTypeHintsPass {
else
Seq(Hint(Text(": ") +: textPartsOf(t, settings.presentationLength, e), e, suffix = true, relatesToPrecedingElement = true))
}

private def getTypeParameters(function: ScMethodLike) = function match {
case fun: ScFunction if !fun.isConstructor => fun.typeParameters
case _: ScFunction | _: ScPrimaryConstructor =>
function.containingClass match {
case td: ScTypeDefinition => td.typeParameters
case _ => Seq.empty
}
case _ => Seq.empty
}

private def collectMethodCalls(expr: ScExpression): List[ScMethodCall] = {
@tailrec
def loop(current: ScExpression, acc: List[ScMethodCall]): List[ScMethodCall] = current match {
case call: ScMethodCall =>
loop(call.getEffectiveInvokedExpr, call :: acc)
case _ => acc
}

loop(expr, Nil)
}
}

private object ScalaTypeHintsPass {
Expand Down
1 change: 1 addition & 0 deletions scala/scala-impl/resources/messages/ScalaBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2339,6 +2339,7 @@ scala.project.settings.form.xray.show=Show:
scala.project.settings.form.xray.parameter.name.hints=Parameter name hints
scala.project.settings.form.xray.by-name.argument.hints=By-name argument hints
scala.project.settings.form.xray.type.hints=Type hints for:
scala.project.settings.form.xray.type.parameter.hints=Type parameters
scala.project.settings.form.xray.member.variables=Member variables
scala.project.settings.form.xray.local.variables=Local variables
scala.project.settings.form.xray.method.results=Method results
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public enum pluginBranch {Release, EAP, Nightly}
public boolean XRAY_SHOW_LAMBDA_PARAMETER_HINTS = true;
public boolean XRAY_SHOW_LAMBDA_PLACEHOLDER_HINTS = true;
public boolean XRAY_SHOW_VARIABLE_PATTERN_HINTS = true;
public boolean XRAY_SHOW_TYPE_PARAMETERS_HINTS = false;
public boolean XRAY_SHOW_METHOD_CHAIN_HINTS = true;
public boolean XRAY_SHOW_IMPLICIT_HINTS = true;
public boolean XRAY_FOR_ALL_PARAMETERS = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
<text resource-bundle="messages/ScalaBundle" key="scala.project.settings.form.xray.widget"/>
</properties>
</component>
<grid id="e4db3" layout-manager="GridLayoutManager" row-count="16" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="e4db3" layout-manager="GridLayoutManager" row-count="17" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="4" column="0" row-span="14" col-span="1" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
Expand All @@ -108,31 +108,31 @@
</component>
<component id="c2735" class="javax.swing.JCheckBox" binding="myShowMethodChainHintsCheckbox">
<constraints>
<grid row="12" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
<grid row="13" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ScalaBundle" key="scala.project.settings.form.xray.method.chain.hints"/>
</properties>
</component>
<component id="11b9" class="javax.swing.JCheckBox" binding="myShowIndentGuidesCheckbox">
<constraints>
<grid row="14" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
<grid row="15" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ScalaBundle" key="scala.project.settings.form.xray.indent.guides"/>
</properties>
</component>
<component id="2e3dd" class="javax.swing.JCheckBox" binding="myShowMethodSeparatorsCheckbox">
<constraints>
<grid row="15" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
<grid row="16" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ScalaBundle" key="scala.project.settings.form.xray.method.separators"/>
</properties>
</component>
<component id="cb34d" class="javax.swing.JCheckBox" binding="myShowImplicitHintsCheckbox">
<constraints>
<grid row="13" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
<grid row="14" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="1" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ScalaBundle" key="scala.project.settings.form.xray.implicit.hints"/>
Expand Down Expand Up @@ -227,6 +227,14 @@
<text resource-bundle="messages/ScalaBundle" key="scala.project.settings.form.xray.apply.method.hints"/>
</properties>
</component>
<component id="3a00f" class="javax.swing.JCheckBox" binding="myShowTypeParametersCheckbox">
<constraints>
<grid row="12" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/ScalaBundle" key="scala.project.settings.form.xray.type.parameter.hints"/>
</properties>
</component>
</children>
</grid>
</children>
Expand Down
Loading
Loading