override def typeText()

in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/ScalaTypePresentation.scala [42:476]


  override def typeText(
    `type`: ScType,
    nameRenderer: NameRenderer,
    options: PresentationOptions
  )(implicit tpc: TypePresentationContext, context: Context): String = {
    val textEscaper: TextEscaper = nameRenderer
    val boundsRenderer = new TypeBoundsRenderer(textEscaper)

    def typesText(types: Iterable[ScType])
                 (implicit tpc: TypePresentationContext): String = types
      .map(innerTypeText(_))
      .commaSeparated(model = Model.Parentheses)

    def typeTail(need: Boolean) = if (need) ObjectTypeSuffix else ""

    def typeParametersText(paramsOwner: ScTypeParametersOwner, substitutor: ScSubstitutor)
                          (implicit tpc: TypePresentationContext): String = paramsOwner.typeParameters match {
      case Seq()  => ""
      case params => params.map(typeParamText(_, substitutor)).commaSeparated(model = Model.SquareBrackets)
    }

    def typeParamText(
      param: ScTypeParam,
      substitutor: ScSubstitutor
    )(implicit tpc: TypePresentationContext): String = {
      val typeRenderer: TypeRenderer = t => typeText(substitutor(t), nameRenderer, options)
      val typeParamsRenderer = new TypeParamsRenderer(typeRenderer, boundsRenderer)
      typeParamsRenderer.render(param)
    }

    def projectionTypeText(
      projType: ScProjectionType,
      needDotType: Boolean
    )(implicit tpc: TypePresentationContext): String = {
      val e = projType.actualElement

      def checkIfStable(element: PsiElement): Boolean = element match {
        case _: ScObject | _: ScBindingPattern | _: ScParameter | _: ScFieldId => true
        case _ => false
      }

      val isStaticJavaClass = e match {
        case c: PsiClass => ScalaPsiUtil.isStaticJava(c)
        case _ => false
      }

      val typeTailForProjection = typeTail(checkIfStable(e) && needDotType)

      object StaticJavaClassHolder {
        def unapply(t: ScType): Option[PsiClass] = t match {
          case ScDesignatorType(clazz: PsiClass)                       => Some(clazz)
          case ParameterizedType(ScDesignatorType(clazz: PsiClass), _) => Some(clazz)
          case ScProjectionType(_, clazz: PsiClass)                    => Some(clazz)
          case _                                                       => None
        }
      }

      val refName = e.name
      val escapedName =
        if (options.renderProjectionTypeName) nameRenderer.renderName(e)
        else nameRenderer.escapeName(refName)

      val isElementResolvedFromContextWithoutQualifier = tpc.nameResolvesTo(refName + typeTailForProjection, e)
      if (isElementResolvedFromContextWithoutQualifier) {
        // If the reference can be resolved from the context, we do not render a redundant context prefix (qualifier)
        // NOTE: this logic might be fragile. It heavily depends on the carefully passed type presentation context (tpc).
        // If a wrong presentation context is passed, this logic might wrongfully remove the qualifier leading to various bugs during type inference.
        // Such as Cycles (e.g. StackOverflowError) or just incorrectly-inferred type from a wrong context.
        escapedName + typeTailForProjection
      } else
        projType.projected match {
          case ScDesignatorType(pack: PsiPackage) =>
            nameRenderer.renderNameWithPoint(pack) + escapedName
          case ScDesignatorType(named) if checkIfStable(named) =>
            nameRenderer.renderNameWithPoint(named) + escapedName + typeTailForProjection
          case ScThisType(obj: ScObject) =>
            nameRenderer.renderNameWithPoint(obj) + escapedName + typeTailForProjection
          case p@ScThisType(_: ScTypeDefinition) if checkIfStable(e) =>
            s"${innerTypeText(p, needDotType = false)}.$escapedName$typeTailForProjection"
          case p: ScProjectionType if checkIfStable(p.actualElement) =>
            s"${projectionTypeText(p, needDotType = false)}.$escapedName$typeTailForProjection"
          case StaticJavaClassHolder(clazz) if isStaticJavaClass =>
            nameRenderer.renderNameWithPoint(clazz) + escapedName
          case p@(_: ScCompoundType | _: ScExistentialType) =>
            s"(${innerTypeText(p)})#$escapedName"
          case p =>
            val innerText = innerTypeText(p)
            if (innerText.endsWith(ObjectTypeSuffix)) innerText.stripSuffix("type") + escapedName
            else s"$innerText#$escapedName"
        }
    }

    def compoundTypeText(compType: ScCompoundType)
                        (implicit tpc: TypePresentationContext): String = {
      val ScCompoundType(comps, signatureMap, typeMap) = compType

      val allSignatures = signatureMap ++ typeMap

      def typeText0(tp: ScType, presentationContextElement: PsiNamedElement): String = {
        // We update the presentation context only when we know it's psi-based
        // Otherwise we assume that the presentation context is "Empty", meaning that all the types should be fully-qualified.
        // In this case the `result` of `nameResolvesTo` doesn't matter; we assume that it will be false for all types.
        // This logic was primarily added to adress SCL-24691
        val tpcNew = tpc match {
          case _: TypePresentationContext.PsiBased =>
            TypePresentationContext(presentationContextElement)
          case _ =>
            tpc
        }
        innerTypeText(tp)(tpcNew)
      }

      val componentsText: Seq[String] =
        if (comps.isEmpty || comps == Seq(projectContext.stdTypes.AnyRef))
          Nil
        else {
          val compsTexts = comps.map {
            case tp@FunctionType(_, _) => "(" + innerTypeText(tp) + ")"
            case tp => innerTypeText(tp)
          }
          val compsText = compsTexts.mkString(tpc.compoundTypeSeparatorText)
          Seq(compsText)
        }

      val declsTexts = allSignatures.flatMap {
        case (s: TermSignature, returnType: ScType) if s.namedElement.is[ScFunction] =>
          val function = s.namedElement.asInstanceOf[ScFunction]
          val substitutor = s.substitutor

          val paramClauses: String = {
            val typeRenderer: TypeRenderer = t => typeText0(substitutor(t), function)
            val paramRenderer = new ParameterRenderer(
              typeRenderer,
              ModifiersRenderer.SimpleText(textEscaper),
              new TypeAnnotationRenderer(typeRenderer, ParameterTypeDecorator.DecorateAll),
              textEscaper,
              withMemberModifiers = false,
              withAnnotations = true
            )
            val paramsRenderer = new ParametersRenderer(
              paramRenderer,
              shouldRenderImplicitModifier = true
            )
            paramsRenderer.renderClauses(function)
          }

          val retType = if (!compType.equiv(returnType)) typeText0(substitutor(returnType), function) else s"this$ObjectTypeSuffix"

          val typeParameters = typeParametersText(function, substitutor)

          Some(s"def ${s.name}$typeParameters$paramClauses: $retType")
        case (s: TermSignature, returnType: ScType) if s.namedElement.is[ScTypedDefinition] =>
          val substitutor = s.substitutor
          val named: Option[ScTypedDefinition] = s.namedElement match {
            case _ if s.paramLength > 0 => None
            case pattern: ScBindingPattern => Some(pattern)
            case fieldId: ScFieldId => Some(fieldId)
            case _ => None
          }

          named.map { typedDefinition =>
            val needSpaceAfterName = typedDefinition.name.lastOption.exists(c => !c.isLetterOrDigit && c != '`')
            val spaceAfterName = if (needSpaceAfterName) " " else ""
            val keyword = if (typedDefinition.isVar) "var" else "val"
            val typeAnnotation = s"${typeText0(substitutor(returnType), s.namedElement)}"
            keyword + s" ${typedDefinition.name}$spaceAfterName: $typeAnnotation"
          }
        case (_: String, signature: TypeAliasSignature) =>
          val alias = signature.typeAlias
          val defnText: String =
            if (signature.isDefinition)
              s" = ${typeText0(signature.upperBound, alias)}"
            else {
              val lowerBoundText = boundsRenderer.lowerBoundText(signature.lowerBound)(typeText0(_, alias))
              val upperBoundText = boundsRenderer.upperBoundText(signature.upperBound)(typeText0(_, alias))
              lowerBoundText + upperBoundText
            }

          val typeParameters = typeParametersText(alias, signature.substitutor)
          Some(s"type ${signature.name}$typeParameters$defnText")
        case _ => None
      }

      val refinementText = if (declsTexts.isEmpty) Nil else Seq(declsTexts.mkString("{ ", "; ", " }"))

      (componentsText ++ refinementText).mkString(" ")
    }

    @tailrec
    def existentialTypeText(existentialType: ScExistentialType, checkWildcard: Boolean, stable: Boolean)
                           (implicit tpc: TypePresentationContext): String = {
      def existentialArgWithBounds(wildcard: ScExistentialArgument, name: String): String = {
        val argsText = wildcard.typeParameters.map(_.name) match {
          case Seq() => ""
          case parameters => parameters.commaSeparated(model = Model.SquareBrackets)
        }

        val lowerBound = boundsRenderer.lowerBoundText(wildcard.lower)(innerTypeText(_))
        val upperBound = boundsRenderer.upperBoundText(wildcard.upper)(innerTypeText(_))
        s"$name$argsText$lowerBound$upperBound"
      }

      def placeholder(wildcard: ScExistentialArgument) =
        existentialArgWithBounds(wildcard, if (tpc.compoundTypeWithAndToken) "?" else "_")

      def namedExistentials(wildcards: Seq[ScExistentialArgument]) =
        wildcards.map { wildcard =>
          existentialArgWithBounds(wildcard, s"type ${wildcard.name}")
        }.mkString(" forSome {", "; ", "}")

      existentialType match {
        case ScExistentialType(q, Seq(w)) if checkWildcard =>
          if (q == w) placeholder(w)
          else existentialTypeText(existentialType, checkWildcard = false, stable)
        case ScExistentialType(quant @ ParameterizedType(_, typeArgs), wildcards) =>
          val usedMoreThanOnce = ScExistentialArgument.usedMoreThanOnce(quant)

          def mayBePlaceholder(arg: ScExistentialArgument): Boolean =
            !usedMoreThanOnce(arg) && typeArgs.contains(arg) && arg.typeParameters.isEmpty

          val (placeholders, namedWildcards) = wildcards.partition(mayBePlaceholder)

          val prefix = parameterizedTypeText(quant) {
            case arg: ScExistentialArgument  => if (placeholders.contains(arg)) placeholder(arg) else arg.name
            case t                           => innerTypeText(t, needDotType = true, checkWildcard)
          }

          if (namedWildcards.isEmpty) prefix
          else s"($prefix)${namedExistentials(namedWildcards)}"
        case ex: ScExistentialType =>
          s"(${innerTypeText(ex.quantified)})${namedExistentials(ex.wildcards)}"
      }
    }

    object InfixDesignator {
      private[this] val showAsInfixAnnotation: String = "scala.annotation.showAsInfix"

      private def mayUseSimpleName(named: PsiNamedElement): Boolean = {
        val simpleName = named.name
        simpleName == nameRenderer.renderName(named) || tpc.nameResolvesTo(simpleName, named)
      }

      private def annotated(named: PsiNamedElement) = named match {
        case c: PsiClass => c.getAnnotations.map(_.getQualifiedName).contains(showAsInfixAnnotation)
        case _           => false
      }

      private def hasOperatorName(named: PsiNamedElement): Boolean = ScalaNamesUtil.isOperatorName(named.name)

      def unapply(des: ScType): Option[PsiNamedElement] = {
        des.extractDesignated(expandAliases = false)
          .filter(mayUseSimpleName)
          .filter(named => annotated(named) || hasOperatorName(named))
      }
    }

    object TypeLambda {
      def unapply(proj: ScProjectionType): Option[String] = proj match {
        case ScProjectionType.withActual(alias: ScTypeAliasDefinition, _)
          if alias.kindProjectorPluginEnabled ||
            (alias.kindProjectorEnabled && alias.typeParameters.forall(_.typeParameters.isEmpty)) =>

          proj.projected match {
            case ScCompoundType(comps, sigs, aliases) if
              comps == Seq(projectContext.stdTypes.AnyRef) && sigs.isEmpty && aliases.contains(alias.name) =>
              Option(KindProjectorSimplifyTypeProjectionInspection.convertToKindProjectorSyntax(alias))
            case _ => None
          }
        case _ => None
      }
    }

    def parameterizedTypeText(p: ParameterizedType)
                             (printArgsFun: ScType => String)
                             (implicit tpc: TypePresentationContext): String = p match {
      case ParameterizedType(InfixDesignator(op), Seq(left, right)) if !ScalaApplicationSettings.PRECISE_TEXT && tpc.infixTypesConsiderPrecedence.isDefined => // SCL-21179
        val opRendered =
          if (options.renderInfixType) nameRenderer.renderName(op)
          else nameRenderer.escapeName(op.name)
        infixTypeText(Infix(op.name), opRendered, left, right, printArgsFun(_))
      case ParameterizedType(des, typeArgs) =>
        innerTypeText(des) + typeArgs.map(printArgsFun(_)).commaSeparated(model = Model.SquareBrackets)
    }

    def infixTypeText(infix: Infix, opRendered: String, left: ScType, right: ScType, printArgsFun: ScType => String)
                     (implicit tpc: TypePresentationContext): String = {
      def toInfix(ty: ScType): Option[Infix] = {
        ty match {
          case ParameterizedType(InfixDesignator(newOp), _) => Some(Infix(newOp.name))
          case _: ScAndType  => Some(Infix("&"))
          case _: ScOrType   => Some(Infix("|"))
          case _ => None
        }
      }

      val leftOp =
        printArgsFun(left).parenthesize(needParenthesis = infix.leftNeedsParenthesis(toInfix(left)))

      val rightOp =
        printArgsFun(right).parenthesize(needParenthesis = infix.rightNeedsParenthesis(toInfix(right)))

      s"$leftOp $opRendered $rightOp"
    }

    def textOf(params: Seq[ScType])
              (implicit tpc: TypePresentationContext)= params match {
      case Seq(fun@FunctionType(_, _)) => innerTypeText(fun).parenthesize()
      case Seq(tup@TupleType(_)) => innerTypeText(tup).parenthesize()
      case Seq(mt: ScMatchType) => innerTypeText(mt).parenthesize()
      case Seq(head) => innerTypeText(head)
      case _ => typesText(params)
    }

    def innerTypeText(
      t: ScType,
      needDotType: Boolean = true,
      checkWildcard: Boolean = false
    )(implicit tpc: TypePresentationContext): String = t match {
      case stdType: StdType if options.renderStdTypes =>
        stdType.extractClass match {
          case Some(clazz) => nameRenderer.renderName(clazz)
          case _           => nameRenderer.escapeName(stdType.name)
        }
      case namedType: NamedType => namedType.name
      case ScAbstractType(tpt, _, _) => tpt.name.capitalize + api.presentation.TypePresentation.ABSTRACT_TYPE_POSTFIX
      case PolyFunctionType(sig, retType) =>
        val typeParamsClause = sig.typeParams.map(_.name).mkString("[", ", ", "]")
        val paramTypes       = sig.substitutedTypes.head.map(_.apply())

        val paramClauseText =
          if (paramTypes.size == 1) {
            val param = paramTypes.head
            if (param.is[ScMatchType]) s"(${innerTypeText(param)})"
            else                       innerTypeText(param)
          } else  paramTypes.map(innerTypeText(_)).mkString("(", ", ", ")")

        s"$typeParamsClause => $paramClauseText => ${innerTypeText(retType)}"
      case TypeLambda(text)          => text
      case FunctionType(ret, params) if !t.isAliasType =>
        val paramsText = textOf(params)
        s"$paramsText ${ScalaPsiUtil.functionArrow} ${innerTypeText(ret)}"
      case ContextFunctionType(ret, params) if !t.isAliasType =>
        val paramsText = textOf(params)
        s"$paramsText ${ScalaPsiUtil.contextFunctionArrow} ${innerTypeText(ret)}"
      case ScThisType(element) =>
        val prefix = element match {
          case clazz: ScTypeDefinition => clazz.name + "."
          case _ => ""
        }

        prefix + "this" + typeTail(needDotType)
      case TupleType(comps) if !ScalaApplicationSettings.PRECISE_TEXT || !t.isAliasType => // SCL-21175
        val precise = ScalaApplicationSettings.PRECISE_TEXT && options.canonicalForm
        comps match {
          case Seq() =>
            if (precise) "_root_.scala.EmptyTuple.type"
            else "EmptyTuple"
          case Seq(head) =>
            val headText = innerTypeText(head)
            val scalaPrefix = if (precise) "_root_.scala." else "" // SCL-21183
            if (TupleType.TupleHList.isCons(t)) {
              if (precise) s"$scalaPrefix*:[$headText, $scalaPrefix.EmptyTuple.type]"
              else s"$headText *: EmptyTuple"
            } else {
              s"${scalaPrefix}Tuple1[$headText]"
            }
          case _ => typesText(comps)
        }
      case nt@NamedTupleType(comps) if !ScalaApplicationSettings.PRECISE_TEXT && NamedTupleType.isUnaliasedNamedTupleType(nt) =>
        comps
          .map { case (name, ty) => s"${NamedTupleType.NameType.from(name).getOrElse(name.presentableText)}: ${innerTypeText(ty)}" }
          .commaSeparated(model = Model.Parentheses)
      case ScDesignatorType(element) =>
        val flag = element match {
          case _: ScObject | _: ScReferencePattern | _: ScParameter => true
          case _ => false
        }

        nameRenderer.renderName(element) + typeTail(flag && needDotType)
      case proj: ScProjectionType =>
        projectionTypeText(proj, needDotType)
      case p: ParameterizedType =>
        parameterizedTypeText(p)(innerTypeText(_, checkWildcard = true))
      case JavaArrayType(argument) => (if (ScalaApplicationSettings.PRECISE_TEXT && options.canonicalForm) "_root_.scala." else "") + s"Array[${innerTypeText(argument)}]" // SCL-21183
      case UndefinedType(tpt, _) => "NotInferred" + tpt.name
      case ScAndType(lhs, rhs) =>
        if (ScalaApplicationSettings.PRECISE_TEXT && options.canonicalForm) {
          val l = innerTypeText(lhs)
          val r = innerTypeText(rhs)
          "_root_.scala.&[" + l + ", " + r + "]"
        } else {
          infixTypeText(Infix("&"), "&", lhs, rhs, innerTypeText(_))
        }
      case ScOrType(lhs, rhs) =>
        if (ScalaApplicationSettings.PRECISE_TEXT && options.canonicalForm) {
          val l = innerTypeText(lhs)
          val r = innerTypeText(rhs)
          "_root_.scala.|[" + l + ", " + r + "]"
        } else {
          infixTypeText(Infix("|"), "|", lhs, rhs, innerTypeText(_))
        }
      case c: ScCompoundType =>
        compoundTypeText(c)
      case ex: ScExistentialType =>
        existentialTypeText(ex, checkWildcard, needDotType)
      case pt@ScTypePolymorphicType(internalType, typeParameters) =>
        def toString(tpe: ScType) = if (ScalaApplicationSettings.PRECISE_TEXT) innerTypeText(tpe) else tpe.toString // SCL-21203
        val typeParametersTexts = typeParameters.map {
          case TypeParameter(parameter, _, lowerType, upperType) =>
            parameter.name + boundsRenderer.lowerBoundText(lowerType)(toString) + boundsRenderer.upperBoundText(upperType)(toString)
        }
        val typeParametersText = typeParametersTexts.commaSeparated(model = Model.SquareBrackets)
        // TODO Custom lambda and polymorphic function types, SCL-20394
        val separator = if (pt.isLambdaTypeElement) TypeLambdaArrowWithSpaces else if (FunctionType.isFunctionType(internalType)) s" ${ScalaPsiUtil.functionArrow} " else " "
        typeParametersText + separator + toString(internalType)
      case mt@ScMethodType(retType, params, _) =>
        implicit val elementScope: ElementScope = mt.elementScope
        innerTypeText(FunctionType(retType, params.map(_.paramType)), needDotType)
      case ScLiteralType(value, _) =>
        value.presentation
      case ScMatchType(scrutinee, cases, _) =>
        val scrutineeText = innerTypeText(scrutinee)

        val caseTexts = cases.map { cs =>
          val (pat, res) = cs.apply()
          "case " + pat + " => " + res
        }

        val parenthesesRequired = scrutinee.is[ScMatchType] || FunctionType.isFunctionType(scrutinee)
        (if (parenthesesRequired) scrutineeText.parenthesize() else scrutineeText)  + " match " + caseTexts.mkString("{ ", "; ", " }")
      case _ => "" //todo
    }

    innerTypeText(`type`)
  }