diff --git a/build.sbt b/build.sbt index a9be12e1..8744b1ab 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ import org.jetbrains.sbtidea.{AutoJbr, JbrPlatform} -lazy val scala213 = "2.13.14" +lazy val scala213 = "2.13.15" lazy val scalaPluginVersion = "2024.3.18" lazy val minorVersion = "0" lazy val buildVersion = sys.env.getOrElse("ZIO_INTELLIJ_BUILD_NUMBER", minorVersion) diff --git a/src/main/scala/zio/intellij/inspections/mistakes/DiscardingZIOValueInspection.scala b/src/main/scala/zio/intellij/inspections/mistakes/DiscardingZIOValueInspection.scala index e103f6a0..b200276b 100644 --- a/src/main/scala/zio/intellij/inspections/mistakes/DiscardingZIOValueInspection.scala +++ b/src/main/scala/zio/intellij/inspections/mistakes/DiscardingZIOValueInspection.scala @@ -3,7 +3,7 @@ package zio.intellij.inspections.mistakes import com.intellij.codeInspection.{LocalInspectionTool, ProblemHighlightType, ProblemsHolder} import org.jetbrains.annotations.Nls import org.jetbrains.plugins.scala.codeInspection.PsiElementVisitorSimple -import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType} +import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType, invocationText} import org.jetbrains.plugins.scala.lang.psi.api.expr.{ScExpression, ScFunctionExpr, ScReferenceExpression} import org.jetbrains.plugins.scala.lang.psi.api.statements.ScFunction import org.jetbrains.plugins.scala.lang.psi.types.ScType @@ -12,7 +12,7 @@ import org.jetbrains.plugins.scala.lang.psi.types.result.Typeable import zio.intellij.inspections.mistakes.DiscardingZIOValueInspectionBase.ReturnType import zio.intellij.inspections.streamMethods.`.runDrain` import zio.intellij.inspections.zioMethods.`.map` -import zio.intellij.inspections.{ZInspection, invocationTextFor, lambda} +import zio.intellij.inspections.{ZInspection, lambda} import zio.intellij.utils.StringUtils.ScExpressionExt import zio.intellij.utils.TypeCheckUtils.{`ZStream[R, E, O]`, fromZioLike} import zio.intellij.utils.fromSameClass @@ -61,7 +61,7 @@ object DiscardingZIOValueMapToFlatMapSmartInspection extends SimplificationType } protected def replacement(expr: ScExpression, qual: ScExpression, body: ScExpression): Simplification = - replace(expr).withText(invocationTextFor(qual, s"$replacement${body.getWrappedText}")) + replace(expr).withText(invocationText(qual, s"$replacement${body.getWrappedText}")).highlightFrom(qual) } diff --git a/src/main/scala/zio/intellij/inspections/package.scala b/src/main/scala/zio/intellij/inspections/package.scala index fbfc0261..3600a4d0 100644 --- a/src/main/scala/zio/intellij/inspections/package.scala +++ b/src/main/scala/zio/intellij/inspections/package.scala @@ -16,42 +16,6 @@ import zio.intellij.utils.types._ package object inspections { - // copied over from the Scala plugin because of some upstream changes - // TODO consider removing in the future, replacing with whatever is the new thing - // also, renaming from just `invocationText` to ensure using the correct (our) method - def invocationTextFor(qual: PsiElement, methName: String, args: ScExpression*): String = { - def argsText = argListText(args) - - if (qual == null) { - s"$methName$argsText" - } else { - val qualText = qual.getText - qual match { - case _ childOf ScInfixExpr(`qual`, _, _) if args.size == 1 => - s"$qualText $methName ${args.head.getText}" - case _ childOf ScPostfixExpr(`qual`, _) if args.size == 1 => - s"$qualText $methName ${args.head.getText}" - case _: ScInfixExpr => s"($qualText).$methName$argsText" - case _: ScFor => s"($qualText).$methName$argsText" - case _ => s"$qualText.$methName$argsText" - } - - } - } - - def argListText(args: Seq[ScExpression]): String = { - args match { - case Seq(p: ScParenthesisedExpr) => p.getText - case Seq(b @ ScBlock(_: ScFunctionExpr)) => b.getText - case Seq(ScBlock(stmt: ScBlockStatement)) => s"(${stmt.getText})" - case Seq(b: ScBlock) => b.getText - case Seq((_: ScFunctionExpr) childOf (b: ScBlockExpr)) => b.getText - case Seq(other) => s"(${other.getText})" - case seq if seq.size > 1 => seq.map(_.getText).mkString("(", ", ", ")") - case _ => "" - } - } - object collectionMethods { private[inspections] val `.map` = invocation("map").from(likeCollectionClasses) } @@ -212,8 +176,9 @@ package object inspections { def unapply(expr: ScExpression): Option[(ScReferenceExpression, ScExpression)] = expr match { - case MethodRepr(_, _, Some(ref), Seq(e)) => Some((ref, e)) - case _ => None + case MethodRepr(_, _, Some(ref), Seq(e)) => Some((ref, e)) + case MethodRepr(_, Some(ref: ScReferenceExpression), _, Seq(e)) => Some((ref, e)) + case _ => None } } diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyBuildUseInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyBuildUseInspection.scala index fad2c781..3aaaae14 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyBuildUseInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyBuildUseInspection.scala @@ -1,11 +1,11 @@ package zio.intellij.inspections.simplifications -import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType} +import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType, invocationText} import org.jetbrains.plugins.scala.lang.psi.api.expr.{ScExpression, ScReferenceExpression, ScUnderscoreSection} import zio.intellij.inspections.layerMethods.`.build` import zio.intellij.inspections.managedMethods.`.use` import zio.intellij.inspections.zioMethods.`.provide` -import zio.intellij.inspections.{ZInspection, invocationTextFor, lambda} +import zio.intellij.inspections.{ZInspection, lambda} class SimplifyBuildUseInspection extends ZInspection(BuildUseSimplificationType) @@ -13,7 +13,7 @@ object BuildUseSimplificationType extends SimplificationType { override def hint: String = "Replace with .provideLayer" def replacement(expr: ScExpression, zio: ScExpression, layer: ScExpression): Simplification = - replace(expr).withText(invocationTextFor(zio, "provideLayer", layer)).highlightAll + replace(expr).withText(invocationText(zio, "provideLayer", layer)).highlightAll override def getSimplification(expr: ScExpression): Option[Simplification] = expr match { diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyExitCodeInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyExitCodeInspection.scala index 4fcc1627..4eb7bec7 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyExitCodeInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyExitCodeInspection.scala @@ -1,6 +1,6 @@ package zio.intellij.inspections.simplifications -import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType} +import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType, invocationText} import org.jetbrains.plugins.scala.lang.psi.api.expr.ScExpression import zio.intellij.inspections._ import zio.intellij.inspections.zioMethods._ @@ -12,7 +12,7 @@ object ExitCodeSimplificationType extends SimplificationType { override def getSimplification(expr: ScExpression): Option[Simplification] = { def replacement(qual: ScExpression) = - replace(expr).withText(invocationTextFor(qual, s"exitCode")) + replace(expr).withText(invocationText(qual, s"exitCode")).highlightFrom(qual) expr match { case qual `.map` `_ => x`(exitCodeSuccess() | exitCodeFailure()) => Some(replacement(qual)) diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyFlattenInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyFlattenInspection.scala index b2862e8b..e7675117 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyFlattenInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyFlattenInspection.scala @@ -1,10 +1,10 @@ package zio.intellij.inspections.simplifications -import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType} +import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType, invocationText} import org.jetbrains.plugins.scala.extensions.BooleanExt import org.jetbrains.plugins.scala.lang.psi.api.expr.ScExpression import org.jetbrains.plugins.scala.lang.psi.impl.ScalaPsiElementFactory -import zio.intellij.inspections.{ZInspection, invocationTextFor} +import zio.intellij.inspections.ZInspection import zio.intellij.inspections.zioMethods._ class SimplifyFlattenInspection extends ZInspection(MapFlattenInspection) @@ -15,7 +15,7 @@ object MapFlattenInspection extends SimplificationType { override def getSimplification(expr: ScExpression): Option[Simplification] = expr match { case `.flatten`(qual `.map` f) => - val newText = invocationTextFor(qual, "flatMap", f) + val newText = invocationText(qual, "flatMap", f) sameType(expr, newText).option { replace(expr).withText(newText).highlightFrom(qual) } diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyIgnoreInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyIgnoreInspection.scala index 291a24f4..1f8389ff 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyIgnoreInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyIgnoreInspection.scala @@ -11,7 +11,7 @@ object IgnoreSimplificationType extends SimplificationType { override def hint: String = "Replace with .ignore" override def getSimplification(expr: ScExpression): Option[Simplification] = { - def replacement(qual: ScExpression) = replace(expr).withText(invocationTextFor(qual, "ignore")) + def replacement(qual: ScExpression) = replace(expr).withText(invocationText(qual, "ignore")).highlightFrom(qual) expr match { case qual `.catchAll` `_ => ZIO.unit`() => Some(replacement(qual)) case qual `.foldCause` (`_ => ()`(), `_ => ()`()) => Some(replacement(qual)) diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyInfallibleEffectInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyInfallibleEffectInspection.scala index 2eacb203..7bf21f35 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyInfallibleEffectInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyInfallibleEffectInspection.scala @@ -1,10 +1,6 @@ package zio.intellij.inspections.simplifications -import org.jetbrains.plugins.scala.codeInspection.collections.{ - Qualified, - Simplification, - SimplificationType -} +import org.jetbrains.plugins.scala.codeInspection.collections.{Qualified, Simplification, SimplificationType, invocationText} import org.jetbrains.plugins.scala.lang.psi.api.expr.{ScExpression, ScReferenceExpression} import org.jetbrains.plugins.scala.lang.psi.api.statements.ScFunction import org.jetbrains.plugins.scala.lang.psi.types.result.Typeable @@ -70,7 +66,7 @@ sealed abstract class BaseErrorModificationSimplificationType(qual: Qualified, m val hint = s"Replace with .$methodName" private def replacement(expr: ScExpression, zio: ScExpression, g: ScExpression): Simplification = - replace(expr).withText(invocationTextFor(zio, methodName, g)).highlightAll + replace(expr).withText(invocationText(zio, methodName, g)).highlightAll override def getSimplification(expr: ScExpression): Option[Simplification] = expr match { @@ -98,7 +94,7 @@ sealed abstract class BaseErrorRecoverySimplificationType(qual: Qualified, metho val hint = s"Replace with .map($methodStr)" private def replacement(expr: ScExpression, zio: ScExpression): Option[Simplification] = - createExpression(methodStr, expr).map(m => replace(expr).withText(invocationTextFor(zio, "map", m)).highlightAll) + createExpression(methodStr, expr).map(m => replace(expr).withText(invocationText(zio, "map", m)).highlightAll) override def getSimplification(expr: ScExpression): Option[Simplification] = expr match { diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyMapBothInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyMapBothInspection.scala index 52dad990..179e8a2d 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyMapBothInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyMapBothInspection.scala @@ -19,7 +19,7 @@ object MapBothSimplificationType extends SimplificationType { override def getSimplification(expr: ScExpression): Option[Simplification] = { def replacement(qual: ScExpression, a: ScExpression, b: ScExpression) = replace(expr) - .withText(invocationTextFor(qual, s"mapBoth(${b.getBracedText}, ${a.getBracedText})")) + .withText(invocationText(qual, s"mapBoth(${b.getBracedText}, ${a.getBracedText})")) .highlightFrom(qual) def toFunctionExpr(e: ScExpression): ScExpression = diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyMapInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyMapInspection.scala index 081bc3bd..345cf95d 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyMapInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyMapInspection.scala @@ -13,7 +13,7 @@ object AsSimplificationType extends SimplificationType { override def getSimplification(expr: ScExpression): Option[Simplification] = { def replacement(qual: ScExpression, arg: ScExpression) = - replace(expr).withText(invocationTextFor(qual, s"as${arg.getWrappedText}")) + replace(expr).withText(invocationText(qual, s"as${arg.getWrappedText}")).highlightFrom(qual) expr match { case qual `.map` `_ => x`(x) => Some(replacement(qual, x)) case _ => None @@ -26,7 +26,7 @@ object MapErrorSimplificationType extends SimplificationType { override def getSimplification(expr: ScExpression): Option[Simplification] = { def replacement(qual: ScExpression, arg: ScExpression) = - replace(expr).withText(invocationTextFor(qual, s"orElseFail${arg.getWrappedText}")) + replace(expr).withText(invocationText(qual, s"orElseFail${arg.getWrappedText}")).highlightFrom(qual) expr match { case qual `.mapError` `_ => x`(x) => Some(replacement(qual, x)) case _ => None diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyOrElseInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyOrElseInspection.scala index 52301032..f3ae27b5 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyOrElseInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyOrElseInspection.scala @@ -1,9 +1,9 @@ package zio.intellij.inspections.simplifications -import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType} +import org.jetbrains.plugins.scala.codeInspection.collections.{Simplification, SimplificationType, invocationText} import org.jetbrains.plugins.scala.lang.psi.api.expr.{ScBlock, ScBlockStatement, ScExpression} import zio.intellij.inspections.zioMethods.`.orElse` -import zio.intellij.inspections.{ZInspection, `ZIO.fail`, invocationTextFor} +import zio.intellij.inspections.{ZInspection, `ZIO.fail`} class SimplifyOrElseInspection extends ZInspection(OrElseFailSimplificationType) @@ -13,22 +13,22 @@ object OrElseFailSimplificationType extends SimplificationType { override def hint: String = s"Replace with .$replaceWith" override def getSimplification(expr: ScExpression): Option[Simplification] = { - def blockReplacement(zio: ScExpression, body: Seq[ScBlockStatement]): Simplification = { + def blockReplacement(qual: ScExpression, body: Seq[ScBlockStatement]): Simplification = { // new Intellij version doesn't seem to like Windows line separators // if ScalaPsiElementFactory.createBlockWithGivenExpressions can use "\n", so can we val separator = "\n" val blockBody = body.map(_.getText).mkString(separator, separator, separator) - replace(expr).withText(s"${zio.getText}.$replaceWith {$blockBody}") + replace(expr).withText(s"${qual.getText}.$replaceWith {$blockBody}").highlightFrom(qual) } - def replacement(zio: ScExpression, error: ScExpression): Simplification = - replace(expr).withText(invocationTextFor(zio, replaceWith, error)) + def replacement(qual: ScExpression, error: ScExpression): Simplification = + replace(expr).withText(invocationText(qual, replaceWith, error)).highlightFrom(qual) expr match { - case zio `.orElse` `ZIO.fail`(_, error) => Some(replacement(zio, error)) - case zio `.orElse` (block: ScBlock) => + case qual `.orElse` `ZIO.fail`(_, error) => Some(replacement(qual, error)) + case qual `.orElse` (block: ScBlock) => Option(block.statements).collect { - case statements :+ `ZIO.fail`(_, error) => blockReplacement(zio, statements :+ error) + case statements :+ `ZIO.fail`(_, error) => blockReplacement(qual, statements :+ error) } case _ => None } diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifySleepInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifySleepInspection.scala index d1016ab9..fd6bbffb 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifySleepInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifySleepInspection.scala @@ -13,7 +13,7 @@ object SleepSimplificationType extends SimplificationType { override def getSimplification(expr: ScExpression): Option[Simplification] = { def replacement(qual: ScExpression, duration: ScExpression) = - replace(expr).withText(invocationTextFor(qual, s"delay${duration.getWrappedText}")).highlightAll + replace(expr).withText(invocationText(qual, s"delay${duration.getWrappedText}")).highlightAll expr match { case `ZIO.sleep`(_, duration) `.*>` io => Some(replacement(io, duration)) case `ZIO.sleep`(_, duration) `.flatMap` lambda(_, io) => Some(replacement(io, duration)) diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyTapInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyTapInspection.scala index 750d0778..836c4935 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyTapInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyTapInspection.scala @@ -71,7 +71,7 @@ sealed abstract class BaseRefactoringType(invocation: Qualified, replaceWith: St override def getSimplification(expr: ScExpression): Option[Simplification] = { def replacement(qual: ScExpression, param: ScParameter, body: ScExpression) = replace(expr) - .withText(invocationTextFor(qual, s"$replaceWith(${simplifyFunctionCall(param, body)})")) + .withText(invocationText(qual, s"$replaceWith(${simplifyFunctionCall(param, body)})")) .highlightFrom(qual) expr match { @@ -100,7 +100,7 @@ object TapBothSimplificationType extends SimplificationType { override def getSimplification(expr: ScExpression): Option[Simplification] = { def replacement(qual: ScExpression, tapError: ScExpression, tap: ScExpression) = replace(expr) - .withText(invocationTextFor(qual, s"tapBoth(${tapError.getBracedText}, ${tap.getBracedText})")) + .withText(invocationText(qual, s"tapBoth(${tapError.getBracedText}, ${tap.getBracedText})")) .highlightFrom(qual) expr match { diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyToLayerInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyToLayerInspection.scala index 01a8a9fd..9a778521 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyToLayerInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyToLayerInspection.scala @@ -18,7 +18,7 @@ sealed abstract class BaseToLayerSimplificationType(methodName: String, methodEx case methodExtractor(_, effect) if fromZio(effect) => val replacementText = effect match { case _: ScFor => s"${effect.getText.parenthesize(true)}.$methodName" - case _ => invocationTextFor(effect, methodName) + case _ => invocationText(effect, methodName) } Some(replace(expr).withText(replacementText).highlightFrom(expr)) case _ => None diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyUnitInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyUnitInspection.scala index fbe86de1..b2004fe0 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyUnitInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyUnitInspection.scala @@ -11,7 +11,7 @@ object UnitSimplificationType extends SimplificationType { override def hint: String = "Replace with .unit" override def getSimplification(expr: ScExpression): Option[Simplification] = { - def replacement(qual: ScExpression) = replace(expr).withText(invocationTextFor(qual, "unit")) + def replacement(qual: ScExpression) = replace(expr).withText(invocationText(qual, "unit")).highlightFrom(qual) expr match { case qual `.*>` `ZIO.unit`(_, _) => Some(replacement(qual)) case qual `.as` unit() => Some(replacement(qual)) diff --git a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyZipInspection.scala b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyZipInspection.scala index 80c77dbe..37ddbd7b 100644 --- a/src/main/scala/zio/intellij/inspections/simplifications/SimplifyZipInspection.scala +++ b/src/main/scala/zio/intellij/inspections/simplifications/SimplifyZipInspection.scala @@ -18,7 +18,7 @@ sealed class BaseZipOneSimplificationType(invocation: Qualified, replaceWith: St override def getSimplification(expr: ScExpression): Option[Simplification] = expr match { case qual invocation `_ => x`(x) => - Some(replace(expr).withText(invocationTextFor(qual, s"$replaceWith${x.getWrappedText}"))) + Some(replace(expr).withText(invocationText(qual, s"$replaceWith${x.getWrappedText}")).highlightFrom(qual)) case _ => None } @@ -33,8 +33,8 @@ sealed class BaseZipOneOperatorSimplificationType(invocation: Qualified, replace def replacement(qual: ScExpression, x: ScExpression) = x match { - case _: ScInfixExpr => replace(expr).withText(s"${qual.getBracedText} $replaceWith (${x.getBracedText})") - case _ => replace(expr).withText(s"${qual.getBracedText} $replaceWith ${x.getBracedText}") + case _: ScInfixExpr => replace(expr).withText(s"${qual.getBracedText} $replaceWith (${x.getBracedText})").highlightFrom(qual) + case _ => replace(expr).withText(s"${qual.getBracedText} $replaceWith ${x.getBracedText}").highlightFrom(qual) } expr match { @@ -57,7 +57,7 @@ sealed abstract class BaseZipToSucceedSimplificationType extends SimplificationT * @return a SimplificationBuilder */ protected def simplify(toSimplify: ScExpression, toKeep: ScExpression, zioArg: ScExpression): SimplificationBuilder = - replace(toSimplify).withText(invocationTextFor(toKeep, s"as${zioArg.getWrappedText}")).highlightAll + replace(toSimplify).withText(invocationText(toKeep, s"as${zioArg.getWrappedText}")).highlightAll } object ZipRightSimplificationType extends BaseZipOneSimplificationType(`.flatMap`, "zipRight") diff --git a/src/test/scala/intellij/testfixtures/ScalaAnnotatorQuickFixTestBase.scala b/src/test/scala/intellij/testfixtures/ScalaAnnotatorQuickFixTestBase.scala deleted file mode 100644 index e3aa5bdf..00000000 --- a/src/test/scala/intellij/testfixtures/ScalaAnnotatorQuickFixTestBase.scala +++ /dev/null @@ -1,237 +0,0 @@ -package org.jetbrains.plugins.scala.codeInspection - -import com.intellij.codeInsight.daemon.impl.HighlightInfo -import com.intellij.codeInsight.intention.IntentionAction -import com.intellij.ide.scratch.ScratchRootType -import com.intellij.openapi.fileTypes.LanguageFileType -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiFile -import com.intellij.psi.codeStyle.CodeStyleManager -import org.jetbrains.plugins.scala.base.{ScalaLightCodeInsightFixtureTestCase, SharedTestProjectToken} -import org.jetbrains.plugins.scala.codeInspection.ScalaAnnotatorQuickFixTestBase.{ExpectedHighlight, TestPrepareResult, checkOffset} -import org.jetbrains.plugins.scala.extensions.{HighlightInfoExt, StringExt, executeWriteActionCommand} -import org.jetbrains.plugins.scala.util.MarkersUtils -import org.jetbrains.plugins.scala.{EditorTests, ScalaFileType} -import org.junit.Assert.{assertFalse, assertTrue, fail} -import org.junit.experimental.categories.Category - -import scala.jdk.CollectionConverters.CollectionHasAsScala - -@Category(Array(classOf[EditorTests])) -abstract class ScalaAnnotatorQuickFixTestBase extends ScalaLightCodeInsightFixtureTestCase { - - import ScalaAnnotatorQuickFixTestBase.quickFixes - - protected def testQuickFix(text: String, expected: String, hint: String): Unit = { - val actions = doFindQuickFixes(text, hint) - assertTrue(s"Quick fixes not found: $hint", actions.nonEmpty) - - executeWriteActionCommand() { - actions.foreach(_.invoke(getProject, getEditor, getFile)) - }(getProject) - - val expectedFile = createLightFile(ScalaFileType.INSTANCE, createTestText(expected).withNormalizedSeparator.trim) - - reformatFile(getFile) - reformatFile(expectedFile) - - getFixture.checkResult(expectedFile.getText, /*stripTrailingSpaces = */ true) - } - protected def reformatFile(file: PsiFile): Unit = - executeWriteActionCommand() { - CodeStyleManager.getInstance(getProject).reformat(file) - }(getProject) - - protected def checkNotFixable(text: String, hint: String): Unit = { - checkNotFixable(text, _ == hint) - } - - protected def checkNotFixable(text: String, hintFilter: String => Boolean): Unit = { - val maybeAction = findQuickFix(text, hintFilter) - assertTrue("Quick fix found.", maybeAction.isEmpty) - } - - protected def checkIsNotAvailable(text: String, hint: String): Unit = { - val action = doFindQuickFix(text, hint) - assertFalse("Quick fix is available", action.isAvailable(getProject, getEditor, getFile)) - } - - private def findQuickFix(text: String, hintFilter: String => Boolean): Option[IntentionAction] = { - val actions = findAllQuickFixes(text, failOnEmptyErrors = false) - actions.find(a => hintFilter(a.getText)) - } - - private def doFindQuickFix(text: String, hint: String, failOnEmptyErrors: Boolean = true): IntentionAction = - doFindQuickFixes(text, hint, failOnEmptyErrors).head - - protected def doFindQuickFixes(text: String, hint: String, failOnEmptyErrors: Boolean = true): Seq[IntentionAction] = { - val actions = findAllQuickFixes(text, failOnEmptyErrors) - val actionsMatching = actions.filter(_.getText == hint) - assert(actionsMatching.nonEmpty, s"Quick fixes not found. Available actions:\n${actions.map(_.getText).mkString("\n")}") - actionsMatching - } - - protected def findAllQuickFixes(text: String, failOnEmptyErrors: Boolean = true): Seq[IntentionAction] = { - val highlights = configureByText(text).actualHighlights - if (highlights.isEmpty && failOnEmptyErrors) { - fail("Errors not found.").asInstanceOf[Nothing] - } - else { - highlights.flatMap(quickFixes) - } - } - - protected def description: String - - protected val fileType: LanguageFileType = ScalaFileType.INSTANCE - protected val isScratchFile: Boolean = false - - override protected def sharedProjectToken = SharedTestProjectToken(this.getClass) - - protected def descriptionMatches(s: String): Boolean = s == description.withNormalizedSeparator.trim - - protected override def checkTextHasNoErrors(text: String): Unit = { - val TestPrepareResult(fileText, _, highlights) = configureByText(text) - val ranges = highlights.map(_.range) - - def rangeText = ranges.mkString(", ") - - assertTrue( - if (shouldPass) s"Highlights found at: $rangeText:\n${highlightsDebugText(highlights, fileText)}" - else failingPassed, - !shouldPass ^ ranges.isEmpty - ) - } - - private def highlightsDebugText(highlights: Seq[HighlightInfo], fileText: String): String = { - val strings = highlights.map(highlightsDebugText(_, fileText)) - val indent = " " - strings.mkString(indent, indent + "\n", "") - } - - private def highlightsDebugText(info: HighlightInfo, fileText: String): String = { - val range = info.range - val rangeText = fileText.substring(range.getStartOffset, range.getEndOffset) - s"$range[$rangeText]: ${info.getDescription}" - } - - protected def checkTextHasError(text: String, allowAdditionalHighlights: Boolean = false): Unit = { - val TestPrepareResult(fileText, expectedRanges, actualHighlights) = configureByText(text) - val actualRanges = actualHighlights - checkTextHasError(expectedRanges, actualRanges, allowAdditionalHighlights, fileText) - } - - protected def checkTextHasError(expectedHighlights: Seq[ExpectedHighlight], - actualHighlights: Seq[HighlightInfo], - allowAdditionalHighlights: Boolean, - fileText: String): Unit = { - val expectedHighlightRanges = expectedHighlights.map(_.range) - val actualHighlightRanges = actualHighlights.map(_.range) - - val expectedRangesNotFound = expectedHighlightRanges.filterNot(actualHighlightRanges.contains) - if (shouldPass) { - assertTrue( - s"Highlights not found: $description", - actualHighlightRanges.nonEmpty - ) - assertTrue( - s"""Highlights found at: ${actualHighlightRanges.mkString(", ")} - |not found: ${expectedRangesNotFound.mkString(", ")}""".stripMargin, - expectedRangesNotFound.isEmpty - ) - - assertNoDuplicates(actualHighlights, fileText) - - if (!allowAdditionalHighlights) { - assertTrue( - s"""Found too many highlights: - |${highlightsDebugText(actualHighlights, fileText)} - |expected: ${expectedHighlightRanges.mkString(", ")}""".stripMargin, - actualHighlightRanges.length == expectedHighlightRanges.length - ) - } - } else { - assertTrue(failingPassed, actualHighlightRanges.isEmpty) - assertTrue(failingPassed, expectedRangesNotFound.nonEmpty) - } - } - - private def assertNoDuplicates(highlights: Seq[HighlightInfo], fileText: String): Unit = { - val duplicatedHighlights = highlights - .groupBy(_.range).toSeq - .collect { case (_, highlights) if highlights.size > 1 => highlights } - .flatten - assertTrue( - s"Some highlights were duplicated:\n${highlightsDebugText(duplicatedHighlights, fileText: String)}", - duplicatedHighlights.isEmpty - ) - } - - protected def configureByText(text: String): TestPrepareResult = { - val fileTextNormalized = - createTestText(text).withNormalizedSeparator.trim - - if (isScratchFile) { - val vFile = createScratchFile(fileTextNormalized) - myFixture.configureFromExistingVirtualFile(vFile) - } else { - myFixture.configureByText(fileType, fileTextNormalized) - } - - val (_, expectedRanges) = MarkersUtils.extractMarker(fileTextNormalized, START, END, caretMarker = Some(CARET)) - val expectedHighlights = expectedRanges.map(ExpectedHighlight) - - //we calculate caret offset after the editor was configured and all selection markers are stripped away - val explicitCaretMarkerOffset = - if (fileTextNormalized.contains(CARET)) - getEditor.getCaretModel.getOffset - else - -1 - - onFileCreated(myFixture.getFile) - - val highlightsAll = - myFixture.doHighlighting().asScala.toSeq - val highlightsFiltered = - highlightsAll - .filter(highlightInfo => descriptionMatches(highlightInfo.getDescription)) - .filter(checkOffset(_, explicitCaretMarkerOffset)) - - TestPrepareResult(myFixture.getFile.getText, expectedHighlights, highlightsFiltered) - } - - private def createScratchFile(normalizedText: String) = { - val fileName = s"aaa.${fileType.getDefaultExtension}" - val language = fileType.getLanguage - ScratchRootType.getInstance.createScratchFile(getProject, fileName, language, normalizedText) - } - - protected def onFileCreated(file: PsiFile): Unit = () - - protected def createTestText(text: String): String = text -} - -object ScalaAnnotatorQuickFixTestBase { - case class ExpectedHighlight(range: TextRange) - case class TestPrepareResult(fileText: String, expectedHighlights: Seq[ExpectedHighlight], actualHighlights: Seq[HighlightInfo]) - - private def quickFixes(info: HighlightInfo): Seq[IntentionAction] = { - val builder = Seq.newBuilder[IntentionAction] - info.findRegisteredQuickFix { (descriptor, _) => - builder += descriptor.getAction - null - } - builder.result() - } - - private def highlightedRange(info: HighlightInfo): TextRange = - new TextRange(info.getStartOffset, info.getEndOffset) - - private def checkOffset(highlightInfo: HighlightInfo, caretOffset: Int): Boolean = - if (caretOffset == -1) - true - else { - val range = highlightedRange(highlightInfo) - range.containsOffset(caretOffset) - } -} diff --git a/src/test/scala/intellij/testfixtures/package.scala b/src/test/scala/intellij/testfixtures/package.scala deleted file mode 100644 index cf6c4462..00000000 --- a/src/test/scala/intellij/testfixtures/package.scala +++ /dev/null @@ -1,18 +0,0 @@ -package intellij - -import org.jetbrains.plugins.scala.DependencyManagerBase.DependencyDescription -import org.jetbrains.plugins.scala.ScalaVersion - -package object testfixtures { - - implicit class RichStr(private val org: String) extends AnyVal { - - def % (artId: String) = DependencyDescription(org, artId, "") - - def %% (artId: String)(implicit scalaVersion: ScalaVersion) = DependencyDescription( - org, - artId + "_" + scalaVersion.major, - "" - ) - } -} diff --git a/src/test/scala/org/jetbrains/plugins/scala/base/ScalaCompletionAutoPopupTestCase.scala b/src/test/scala/org/jetbrains/plugins/scala/base/ScalaCompletionAutoPopupTestCase.scala index b37b7c35..31141cae 100644 --- a/src/test/scala/org/jetbrains/plugins/scala/base/ScalaCompletionAutoPopupTestCase.scala +++ b/src/test/scala/org/jetbrains/plugins/scala/base/ScalaCompletionAutoPopupTestCase.scala @@ -5,9 +5,11 @@ import com.intellij.openapi.fileTypes.FileType import com.intellij.psi.PsiFile import com.intellij.testFramework.fixtures.CompletionAutoPopupTester import com.intellij.util.ThrowableRunnable -import org.jetbrains.plugins.scala.ScalaFileType +import org.jetbrains.plugins.scala.{CompletionTests, ScalaFileType} +import org.junit.experimental.categories.Category /** @see [[com.intellij.codeInsight.completion.JavaCompletionAutoPopupTestCase]] */ +@Category(Array(classOf[CompletionTests])) abstract class ScalaCompletionAutoPopupTestCase extends ScalaLightCodeInsightFixtureTestCase { private[this] var myTester: CompletionAutoPopupTester = _ diff --git a/src/test/scala/org/jetbrains/plugins/scala/base/ScalaFileSetTestCase.java b/src/test/scala/org/jetbrains/plugins/scala/base/ScalaFileSetTestCase.java index 99f3a6ac..6d5c63ae 100644 --- a/src/test/scala/org/jetbrains/plugins/scala/base/ScalaFileSetTestCase.java +++ b/src/test/scala/org/jetbrains/plugins/scala/base/ScalaFileSetTestCase.java @@ -19,6 +19,7 @@ import com.intellij.lang.Language; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.codeStyle.CodeStyleSettings; @@ -42,6 +43,8 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import java.util.stream.Stream; import static com.intellij.openapi.util.io.FileUtil.loadFileText; @@ -69,17 +72,17 @@ protected boolean needsSdk() { return false; } - private Test constructTestCase(File file) { + protected Test constructTestCase(File file) { if (needsSdk()) return new ActualTest(file); return new NoSdkTestCase(file); } - protected void setUp(@NotNull Project project) { + public void setUp(@NotNull Project project) { setSettings(project); } - protected void tearDown(@NotNull Project project) { + public void tearDown(@NotNull Project project) { } @NotNull @@ -95,16 +98,10 @@ protected Language getLanguage() { //used just to propagate to ActualTest.supportedIn //default implementation took from org.jetbrains.plugins.scala.base.ScalaSdkOwner.supportedIn //TODO: consider using Scala 2.13 by default - protected boolean supportedInScalaVersion(ScalaVersion version) { + public boolean supportedInScalaVersion(ScalaVersion version) { return true; } - @NotNull - @Override - public final String getName() { - return getClass().getName(); - } - protected void setSettings(@NotNull Project project) { CommonCodeStyleSettings.IndentOptions indentOptions = getCommonSettings(project).getIndentOptions(); assertNotNull(indentOptions); @@ -134,52 +131,38 @@ protected String transformExpectedResult(@NotNull String text) { return text; } - protected void runTest(@NotNull final String testName0, - @NotNull final String content0, + // Notice that it doesn't include the new line before and after the line with the "----" separator + private static final Pattern FILE_PARTS_SEPARATOR_PATTERN = Pattern.compile("\\n?(?m)^-{4,}\\n?"); + + /** + * @return a list of sections from the test file, split by dashes and trimmed + */ + protected final List parseTestFileText(String textFileText) { + // with the "limit =-1" argument, an empty content after the separator (including the new line) + // will be treated as an empty string "" + String[] parts = FILE_PARTS_SEPARATOR_PATTERN.split(StringUtil.convertLineSeparators(textFileText), -1); + return Arrays.stream(parts).collect(Collectors.toList()); + } + + protected void runTest(@NotNull final String testName, + @NotNull final String testFileText, @NotNull final Project project) { - final List input = new ArrayList<>(); - - int separatorIndex; - // Adding input before ----- - String content = content0; - while ((separatorIndex = content.indexOf("-----")) >= 0) { - input.add(content.substring(0, separatorIndex - 1)); - content = content.substring(separatorIndex); - while (startsWithChar(content, '-') || - startsWithChar(content, '\n')) { - content = content.substring(1); - } - } + List fileParts = parseTestFileText(testFileText); - // Result - after ----- - String result = content; - while (startsWithChar(result, '-') || - startsWithChar(result, '\n') || - startsWithChar(result, '\r')) { - result = result.substring(1); - } + assertTrue("Test file should have at least two sections separated with ----", fileParts.size() > 1); - if (result.trim().equalsIgnoreCase("UNCHANGED_TAG")) { - assertEquals("Unchanged expected result expects only 1 input entry", 1, input.size()); - result = input.get(0); - } + final String inputRaw = fileParts.get(0); + final String expectedResultRaw = fileParts.get(fileParts.size() - 1); - assertFalse("No data found in source file", input.isEmpty()); - assertNotNull(result); + final String testNameWithoutDot = testName.split("\\.")[0]; - final String testName; - final int dotIdx = testName0.indexOf('.'); - testName = dotIdx >= 0 ? testName0.substring(0, dotIdx) : testName0; - - String temp = transform(testName, input.get(0), project); - result = transformExpectedResult(result.trim()); - - final String transformed = convertLineSeparators(temp).trim(); + final String actualResult = transform(testNameWithoutDot, inputRaw, project).trim(); + final String expectedResult = transformExpectedResult(expectedResultRaw).trim(); if (shouldPass()) { - assertEquals(result, transformed); + assertEquals(expectedResult, actualResult); } else { - assertNotEquals(result, transformed); + assertNotEquals(expectedResult, actualResult); } } @@ -187,7 +170,7 @@ protected boolean shouldPass() { return true; } - @SuppressWarnings("UnconstructableJUnitTestCase") + @SuppressWarnings("JUnitMalformedDeclaration") @Category({FileSetTests.class}) private final class NoSdkTestCase extends LightJavaCodeInsightFixtureTestCase { private final File testFile; @@ -217,12 +200,13 @@ public void runTestRunnable(@NotNull ThrowableRunnable testRunnable) try { ScalaFileSetTestCase.this.runTest( testFile.getName(), - convertLineSeparators(fileText), + StringUtil.convertLineSeparators(fileText), getProject() ); } catch(Throwable error) { - // to be able to Ctrl + Click in console to nabigate to test file on failure - // (note, can not work with Android plugin disabled, see IDEA-257969) + // to be able to navigate to the original test file location on test failure + // (you can use Ctrl/Cmd + Click in the console) + // (note, might not work with Android plugin disabled, see IDEA-257969) System.err.println("### Test file: " + testFile.getAbsolutePath()); throw error; } @@ -246,7 +230,7 @@ public String getName() { } } - @SuppressWarnings("UnconstructableJUnitTestCase") + @SuppressWarnings("JUnitMalformedDeclaration") @Category({FileSetTests.class}) private final class ActualTest extends ScalaLightCodeInsightFixtureTestCase { @@ -266,7 +250,6 @@ public void setUp() { try { super.setUp(); ScalaFileSetTestCase.this.setUp(getProject()); - TestUtils.disableTimerThread(); } catch (Exception e) { try { tearDown(); @@ -291,12 +274,13 @@ public void runTestRunnable(@NotNull ThrowableRunnable testRunnable) try { ScalaFileSetTestCase.this.runTest( myTestFile.getName(), - convertLineSeparators(fileText), + StringUtil.convertLineSeparators(fileText), getProject() ); } catch(Throwable error) { - // to be able to Ctrl + Click in console to nabigate to test file on failure - // (note, can not work with Android plugin disabled, see IDEA-257969) + // to be able to navigate to the original test file location on test failure + // (you can use Ctrl/Cmd + Click in the console) + // (note, might not work with Android plugin disabled, see IDEA-257969) System.err.println("### Test file: " + myTestFile.getAbsolutePath()); throw error; } @@ -317,7 +301,12 @@ public String toString() { @NotNull @Override public String getName() { - return myTestFile.getAbsolutePath(); + final var name = myTestFile.getName(); + final var dotIndex = name.lastIndexOf('.'); + if (dotIndex == -1) { + return name; + } + return name.substring(0, dotIndex); } } diff --git a/src/test/scala/org/jetbrains/plugins/scala/base/ScalaFixtureTestCase.scala b/src/test/scala/org/jetbrains/plugins/scala/base/ScalaFixtureTestCase.scala index 787cb320..37b2ced8 100644 --- a/src/test/scala/org/jetbrains/plugins/scala/base/ScalaFixtureTestCase.scala +++ b/src/test/scala/org/jetbrains/plugins/scala/base/ScalaFixtureTestCase.scala @@ -1,13 +1,21 @@ package org.jetbrains.plugins.scala package base +import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl +import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.util.registry.Registry -import com.intellij.testFramework.{EditorTestUtil, IndexingTestUtil} -import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase -import org.jetbrains.plugins.scala.base.libraryLoaders.{HeavyJDKLoader, LibraryLoader, ScalaSDKLoader} +import com.intellij.testFramework.TestIndexingModeSupporter.IndexingMode +import com.intellij.testFramework.builders.ModuleFixtureBuilder +import com.intellij.testFramework.fixtures.{CodeInsightFixtureTestCase, IndexingModeCodeInsightTestFixture} +import com.intellij.testFramework.{EditorTestUtil, IdeaTestUtil} +import com.intellij.util.lang.JavaVersion +import org.jetbrains.plugins.scala.base.libraryLoaders.{LibraryLoader, ScalaSDKLoader} +import org.jetbrains.plugins.scala.extensions.inWriteAction -abstract class ScalaFixtureTestCase extends CodeInsightFixtureTestCase with ScalaSdkOwner { +abstract class ScalaFixtureTestCase extends CodeInsightFixtureTestCase[ModuleFixtureBuilder[_]] with ScalaSdkOwner { protected val CARET = EditorTestUtil.CARET_TAG @@ -15,20 +23,50 @@ abstract class ScalaFixtureTestCase extends CodeInsightFixtureTestCase with Scal protected final implicit def projectContext: Project = getProject + protected lazy val jdk: Sdk = IdeaTestUtil.getMockJdk(JavaVersion.compose(17)) + override protected def librariesLoaders: Seq[LibraryLoader] = Seq( ScalaSDKLoader(includeScalaCompilerIntoLibraryClasspath = includeCompilerAsLibrary), - HeavyJDKLoader() + new LibraryLoader { + override def init(implicit module: Module, version: ScalaVersion): Unit = { + val jdkTable = JavaAwareProjectJdkTableImpl.getInstanceEx + inWriteAction(jdkTable.addJdk(jdk)) + ModuleRootModificationUtil.setModuleSdk(module, jdk) + } + + override def clean(implicit module: Module): Unit = { + ModuleRootModificationUtil.setModuleSdk(module, null) + val jdkTable = JavaAwareProjectJdkTableImpl.getInstanceEx + inWriteAction(jdkTable.removeJdk(jdk)) + } + } ) + //start section: indexing mode setup + private[this] var indexingMode: IndexingMode = IndexingMode.SMART + + // SCL-21849 + protected def getIndexingMode: IndexingMode = indexingMode + protected def setIndexingMode(mode: IndexingMode): Unit = indexingMode = mode + //end section: indexing mode setup + override protected def setUp(): Unit = { super.setUp() setUpLibraries(myModule) - IndexingTestUtil.waitUntilIndexesAreReady(getProject) + Registry.get("ast.loading.filter").setValue(true, getTestRootDisposable) } + override def tuneFixture(moduleBuilder: ModuleFixtureBuilder[_]): Unit = { + super.tuneFixture(moduleBuilder) + + indexingMode = this.findIndexingModeAnnotation() + .fold(IndexingMode.SMART)(_.mode()) + myFixture = IndexingModeCodeInsightTestFixture.Companion.wrapFixture(myFixture, indexingMode) + } + override def tearDown(): Unit = { disposeLibraries(myModule) super.tearDown() } -} \ No newline at end of file +} diff --git a/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/ScalaLibraryLoader.scala b/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/ScalaLibraryLoader.scala index cdfe441d..412031a2 100644 --- a/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/ScalaLibraryLoader.scala +++ b/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/ScalaLibraryLoader.scala @@ -16,9 +16,9 @@ import java.{util => ju} * It doesn't load compiler classpath jars and creates a simple library */ final case class ScalaLibraryLoader( - scalaVersion: ScalaVersion, - dependencyManager: DependencyManagerBase = DependencyManager - ) + scalaVersion: ScalaVersion, + dependencyManager: DependencyManagerBase = DependencyManager +) extends LibraryLoader { import DependencyManagerBase._ @@ -71,10 +71,10 @@ object ScalaLibraryLoader { * It uses separate scala libraries with specified versions */ def libraryLoadersWithSeparateScalaLibraries( - superLibraryLoaders: Seq[LibraryLoader], - scala2Version: ScalaVersion, - scala3Version: ScalaVersion, - ): Seq[LibraryLoader] = { + superLibraryLoaders: Seq[LibraryLoader], + scala2Version: ScalaVersion, + scala3Version: ScalaVersion, + ): Seq[LibraryLoader] = { val scala2LibraryLoader = ScalaLibraryLoader(scala2Version) val scala3LibraryLoader = ScalaLibraryLoader(scala3Version) diff --git a/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/ScalaSDKLoader.scala b/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/ScalaSDKLoader.scala index 78fcb2ae..558b5896 100644 --- a/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/ScalaSDKLoader.scala +++ b/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/ScalaSDKLoader.scala @@ -18,17 +18,17 @@ import java.io.File * @param includeScalaLibraryTransitiveDependencies for scala 3 library, also includes scala 2 library */ case class ScalaSDKLoader( - includeScalaReflectIntoCompilerClasspath: Boolean = false, - //TODO: drop this parameter and fix tests - includeScalaCompilerIntoLibraryClasspath: Boolean = false, - includeScalaLibraryTransitiveDependencies: Boolean = true, - includeScalaLibraryFilesInSdk: Boolean = true, - //TODO: by default sources are not needed in all tests - // make it "false" by default, check which tests fail and set it to true in those tests - includeScalaLibrarySources: Boolean = true, - compilerBridgeBinaryJar: Option[File] = None, - dependencyManager: DependencyManagerBase = DependencyManager - ) extends LibraryLoader { + includeScalaReflectIntoCompilerClasspath: Boolean = false, + //TODO: drop this parameter and fix tests + includeScalaCompilerIntoLibraryClasspath: Boolean = false, + includeScalaLibraryTransitiveDependencies: Boolean = true, + includeScalaLibraryFilesInSdk: Boolean = true, + //TODO: by default sources are not needed in all tests + // make it "false" by default, check which tests fail and set it to true in those tests + includeScalaLibrarySources: Boolean = true, + compilerBridgeBinaryJar: Option[File] = None, + dependencyManager: DependencyManagerBase = DependencyManager +) extends LibraryLoader { import DependencyManagerBase._ import ScalaSDKLoader._ diff --git a/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/SmartJDKLoader.scala b/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/SmartJDKLoader.scala index bff61578..38234055 100644 --- a/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/SmartJDKLoader.scala +++ b/src/test/scala/org/jetbrains/plugins/scala/base/libraryLoaders/SmartJDKLoader.scala @@ -6,7 +6,7 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.module.Module import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl import com.intellij.openapi.projectRoots.{JavaSdk, JavaSdkVersion, Sdk} -import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.openapi.roots.{ModuleRootModificationUtil, OrderRootType} import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess import com.intellij.pom.java.LanguageLevel @@ -84,6 +84,55 @@ object SmartJDKLoader { } } + def createJdk(languageLevel: LanguageLevel): Sdk = { + val jdkVersion = JavaSdkVersion.fromLanguageLevel(languageLevel) + val jdkName = jdkVersion.getDescription + createNewJdk(jdkVersion, jdkName) + } + + /** + * Creates a JDK that contains classes from the specified modules without registering it to the ProjectJdkTable. + * Useful in subclasses of [[ScalaLightCodeInsightFixtureTestCase]] which require more classes than the mock JDKs + * contain. + * + * @note [[ScalaLightCodeInsightFixtureTestCase]] automatically registers the project SDK to the JDK table. + */ + def createFilteredJdk( + languageLevel: LanguageLevel, + jdkModuleNames: Seq[String], + includeSourcesAndDocs: Boolean = false + ): Sdk = { + if (languageLevel.isLessThan(LanguageLevel.JDK_1_9)) { + throw new IllegalArgumentException("can only create filtered JDKs based on Java 9+") + } + if (jdkModuleNames.isEmpty) { + throw new IllegalArgumentException("jdkModuleNames cannot be empty") + } + + val jdkVersion = JavaSdkVersion.fromLanguageLevel(languageLevel) + val jdkName = { + val description = jdkVersion.getDescription + if (jdkModuleNames.isEmpty) description else s"$description-${jdkModuleNames.mkString("{", ",", "}")}" + } + + val jdk = createNewJdk(jdkVersion, jdkName) + // Create a Java SDK that only contains the classes from the JDK modules provided as a parameter to this method. + // E.g. the `java.base` JDK module contains the well-known classes such as `java.lang.Object`, + // `java.lang.String`, `java.util.List`, etc... + // Having a minimal number of classes in the created SDK significantly speeds up SDK set up and indexing in tests. + val modulePaths = jdkModuleNames.map(m => s"/$m") + // Keep only classes from the specified modules. + val filterFn = (url: String) => modulePaths.exists(url.endsWith) + // If sources and docs should be included, keep only sources and docs from the specified modules, otherwise remove all sources and docs. + val filterSrcsDocsFn = (url: String) => includeSourcesAndDocs && filterFn(url) + val modificator = jdk.getSdkModificator + modificator.getUrls(OrderRootType.CLASSES).filterNot(filterFn).foreach(modificator.removeRoot(_, OrderRootType.CLASSES)) + modificator.getUrls(OrderRootType.SOURCES).filterNot(filterSrcsDocsFn).foreach(modificator.removeRoot(_, OrderRootType.SOURCES)) + modificator.getUrls(OrderRootType.DOCUMENTATION).filterNot(filterSrcsDocsFn).foreach(modificator.removeRoot(_, OrderRootType.DOCUMENTATION)) + inWriteAction(modificator.commitChanges()) + jdk + } + private def createNewJdk(jdkVersion: JavaSdkVersion, jdkName: String): Sdk = { val pathOption = SmartJDKLoader.discoverJDK(jdkVersion).map(_.getAbsolutePath) Assert.assertTrue(s"Couldn't find $jdkVersion", pathOption.isDefined) diff --git a/src/test/scala/org/jetbrains/plugins/scala/codeInspection/ScalaAnnotatorQuickFixTestBase.scala b/src/test/scala/org/jetbrains/plugins/scala/codeInspection/ScalaAnnotatorQuickFixTestBase.scala new file mode 100644 index 00000000..6c36afbc --- /dev/null +++ b/src/test/scala/org/jetbrains/plugins/scala/codeInspection/ScalaAnnotatorQuickFixTestBase.scala @@ -0,0 +1,113 @@ +package org.jetbrains.plugins.scala.codeInspection + +import com.intellij.codeInsight.daemon.impl.HighlightInfo +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.fileTypes.LanguageFileType +import com.intellij.psi.PsiFile +import org.jetbrains.plugins.scala.base.ScalaLightCodeInsightFixtureTestCase +import org.jetbrains.plugins.scala.codeInspection.ScalaQuickFixTestFixture.{ExpectedHighlight, TestPrepareResult} +import org.jetbrains.plugins.scala.extensions.{HighlightInfoExt, StringExt} +import org.jetbrains.plugins.scala.{EditorTests, ScalaFileType} +import org.junit.Assert.assertTrue +import org.junit.experimental.categories.Category + +/** + * This is a base test to test quick fixes. + * It simply delegates all implementations to [[ScalaQuickFixTestFixture]]. + * If you want to use a different base test class, use [[ScalaQuickFixTestFixture]] directly + */ +@Category(Array(classOf[EditorTests])) +abstract class ScalaAnnotatorQuickFixTestBase extends ScalaLightCodeInsightFixtureTestCase { + + private var scalaQuickFixFixture: ScalaQuickFixTestFixture = _ + + override protected def setUp(): Unit = { + super.setUp() + + scalaQuickFixFixture = new ScalaQuickFixTestFixture( + myFixture, + description, + fileType, + isScratchFile = isScratchFile, + shouldPass = shouldPass, + onFileCreated = onFileCreated, + createTestText = createTestText, + trimExpectedText = trimExpectedText + ) + + scalaQuickFixFixture.descriptionMatcher = descriptionMatches + } + + protected def description: String + protected def fileType: LanguageFileType = ScalaFileType.INSTANCE + protected def isScratchFile: Boolean = false + protected def descriptionMatches(s: String): Boolean = s == description.withNormalizedSeparator.trim + protected def onFileCreated(file: PsiFile): Unit = () + protected def createTestText(text: String): String = text + protected def trimExpectedText: Boolean = true + + protected override def checkTextHasNoErrors(text: String): Unit = { + configureByText(text) + val highlights = findMatchingHighlightings(text) + + val ranges = highlights.map(_.range) + + def rangeText = ranges.mkString(", ") + + assertTrue( + if (shouldPass) s"Highlights found at: $rangeText:\n${scalaQuickFixFixture.highlightsDebugText(highlights, getFile.getText)}" + else failingPassed, + !shouldPass ^ ranges.isEmpty + ) + } + + protected def configureByText(text: String): Seq[ExpectedHighlight] = + scalaQuickFixFixture.configureByText(text) + + protected def findMatchingHighlightings(text: String): Seq[HighlightInfo] = + scalaQuickFixFixture.findMatchingHighlights(text) + + protected def testQuickFix(text: String, expected: String, hint: String): Unit = + scalaQuickFixFixture.testQuickFix(text, expected, hint) + + protected def testQuickFixes(text: String, expected: String, hint: String): Unit = + scalaQuickFixFixture.testQuickFixes(text, expected, hint) + + protected def testQuickFixAllInFile(text: String, expected: String, hint: String): Unit = + scalaQuickFixFixture.testQuickFixAllInFile(text, expected, hint) + + protected def testQuickFixAllInFile(text: String, expected: String, hints: Seq[String]): Unit = + scalaQuickFixFixture.testQuickFixAllInFile(text, expected, hints) + + protected def checkNotFixable(text: String, hint: String): Unit = + scalaQuickFixFixture.checkNotFixable(text, hint) + + protected def checkNotFixable(text: String, hintFilter: String => Boolean): Unit = + scalaQuickFixFixture.checkNotFixable(text, hintFilter) + + protected def checkIsNotAvailable(text: String, hint: String): Unit = + scalaQuickFixFixture.checkIsNotAvailable(text, hint) + + protected def doFindQuickFixes(text: String, hint: String, failOnEmptyErrors: Boolean = true): Seq[IntentionAction] = + scalaQuickFixFixture.doFindQuickFixes(text, hint, failOnEmptyErrors) + + protected def doFindQuickFixes(text: String, hints: Seq[String], failOnEmptyErrors: Boolean): Seq[IntentionAction] = + scalaQuickFixFixture.doFindQuickFixes(text, hints, failOnEmptyErrors) + + protected def findAllQuickFixes(text: String, failOnEmptyErrors: Boolean = true): Seq[IntentionAction] = + scalaQuickFixFixture.findAllQuickFixes(text, failOnEmptyErrors) + + protected def checkTextHasError(text: String, allowAdditionalHighlights: Boolean = false): Unit = + scalaQuickFixFixture.checkTextHasError(text, allowAdditionalHighlights) + + protected def assertTextHasError( + expectedHighlights: Seq[ExpectedHighlight], + actualHighlights: Seq[HighlightInfo], + allowAdditionalHighlights: Boolean, + ): Unit = + scalaQuickFixFixture.assertTextHasError( + expectedHighlights, + actualHighlights, + allowAdditionalHighlights, + ) +} \ No newline at end of file diff --git a/src/test/scala/intellij/testfixtures/ScalaInspectionTestBase.scala b/src/test/scala/org/jetbrains/plugins/scala/codeInspection/ScalaInspectionTestBase.scala similarity index 100% rename from src/test/scala/intellij/testfixtures/ScalaInspectionTestBase.scala rename to src/test/scala/org/jetbrains/plugins/scala/codeInspection/ScalaInspectionTestBase.scala diff --git a/src/test/scala/org/jetbrains/plugins/scala/codeInspection/ScalaQuickFixTestFixture.scala b/src/test/scala/org/jetbrains/plugins/scala/codeInspection/ScalaQuickFixTestFixture.scala new file mode 100644 index 00000000..6f16cd8a --- /dev/null +++ b/src/test/scala/org/jetbrains/plugins/scala/codeInspection/ScalaQuickFixTestFixture.scala @@ -0,0 +1,269 @@ + + +package org.jetbrains.plugins.scala.codeInspection + +import com.intellij.codeInsight.daemon.impl.HighlightInfo +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.ide.scratch.ScratchRootType +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileTypes.LanguageFileType +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiFile +import com.intellij.testFramework.EditorTestUtil +import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import org.jetbrains.plugins.scala.ScalaFileType +import org.jetbrains.plugins.scala.codeInspection.ScalaQuickFixTestFixture.{ExpectedHighlight, checkOffset, findRegisteredQuickFixes} +import org.jetbrains.plugins.scala.extensions.{HighlightInfoExt, NonNullObjectExt, StringExt, executeWriteActionCommand} +import org.jetbrains.plugins.scala.util.MarkersUtils +import org.junit.Assert.{assertFalse, assertTrue, fail} + +import scala.annotation.nowarn +import scala.jdk.CollectionConverters.CollectionHasAsScala + +/** + * @param description this is only used in assertion errors, for actual comparison logic see [[descriptionMatcher]] + * It is lazy (by-name parameter) for historical reasons. + * Some inheritors of ScalaAnnotatorQuickFixTestBase don't implement this method and jut use `???` + * @param shouldPass whether the test should pass
. + * Avoid using this parameter, it's left for legacy tests. + * Instead, specify explicitly what is the expected (even failed) result + */ +final class ScalaQuickFixTestFixture( + baseFixture: CodeInsightTestFixture, + description: => String, + fileType: LanguageFileType = ScalaFileType.INSTANCE, + isScratchFile: Boolean = false, + @deprecated shouldPass: Boolean = true, + onFileCreated: PsiFile => Unit = _ => (), + createTestText: String => String = identity, + trimExpectedText: Boolean = true +) { + + var descriptionMatcher: String => Boolean = _ == description.withNormalizedSeparator.trim + + private def getProject: Project = baseFixture.getProject + private def getEditor: Editor = baseFixture.getEditor + private def getFile: PsiFile = baseFixture.getFile + + protected val failingPassed: String = "Test has passed, but was supposed to fail" + + protected val START: String = EditorTestUtil.SELECTION_START_TAG + protected val END: String = EditorTestUtil.SELECTION_END_TAG + protected val CARET: String = EditorTestUtil.CARET_TAG + + def testQuickFix(text: String, expected: String, hint: String): Unit = { + val action = doFindQuickFix(text, hint) + applyQuickFixesAndCheckExpected(Seq(action), expected) + } + + def testQuickFixes(text: String, expected: String, hint: String): Unit = { + val actions: Seq[IntentionAction] = doFindQuickFixes(text, hint) + applyQuickFixesAndCheckExpected(actions, expected) + } + + def testQuickFixAllInFile(text: String, expected: String, hint: String): Unit = + testQuickFixAllInFile(text, expected, Seq(hint)) + + def testQuickFixAllInFile(text: String, expected: String, hints: Seq[String]): Unit = { + val actions = doFindQuickFixes(text, hints, failOnEmptyErrors = true) + applyQuickFixesAndCheckExpected(actions, expected) + } + + def applyQuickFixesAndCheckExpected( + actions: Seq[IntentionAction], + expected: String + ): Unit = { + executeWriteActionCommand() { + actions.foreach(_.invoke(getProject, getEditor, getFile)) + }(getProject) + + val expectedFileText = createTestText(expected) + val expectedFileTextProcessed = expectedFileText.withNormalizedSeparator.pipeIf(trimExpectedText)(_.trim) + baseFixture.checkResult(expectedFileTextProcessed, true) + } + + def checkNotFixable(text: String, hint: String): Unit = { + checkNotFixable(text, _ == hint) + } + + def checkNotFixable(text: String, hintFilter: String => Boolean): Unit = { + val maybeAction = findQuickFix(text, hintFilter) + assertTrue("Quick fix found.", maybeAction.isEmpty) + } + + def checkIsNotAvailable(text: String, hint: String): Unit = { + val action = doFindQuickFix(text, hint) + assertFalse("Quick fix is available", action.isAvailable(getProject, getEditor, getFile)) + } + + private def findQuickFix(text: String, hintFilter: String => Boolean): Option[IntentionAction] = { + val actions = findAllQuickFixes(text, failOnEmptyErrors = false) + actions.find(a => hintFilter(a.getText)) + } + + private def doFindQuickFix(text: String, hint: String, failOnEmptyErrors: Boolean = true): IntentionAction = + doFindQuickFixes(text, hint, failOnEmptyErrors).head + + def doFindQuickFixes(text: String, hint: String, failOnEmptyErrors: Boolean = true): Seq[IntentionAction] = + doFindQuickFixes(text, Seq(hint), failOnEmptyErrors) + + def doFindQuickFixes(text: String, hints: Seq[String], failOnEmptyErrors: Boolean): Seq[IntentionAction] = { + val actions = findAllQuickFixes(text, failOnEmptyErrors) + val hintSet = hints.toSet + val actionsMatching = actions.filter(a => hintSet.contains(a.getText)) + assert(actionsMatching.nonEmpty, + s"""Quick fixes not found. + |Expected actions: + | ${hints.mkString(" \n")} + |Available actions: + | ${actions.map(_.getText).mkString(" \n")}""".stripMargin + ) + actionsMatching + } + + def findAllQuickFixes(text: String, failOnEmptyErrors: Boolean = true): Seq[IntentionAction] = { + configureByText(text) + + val highlights = findMatchingHighlights(text) + if (highlights.isEmpty && failOnEmptyErrors) { + fail("Errors not found.").asInstanceOf[Nothing] + } + else { + highlights.flatMap(findRegisteredQuickFixes) + } + } + + def highlightsDebugText(highlights: Seq[HighlightInfo], fileText: String): String = { + val strings = highlights.map(highlightsDebugText(_, fileText)) + val indent = " " + strings.mkString(indent, indent + "\n", "") + } + + def highlightsDebugText(info: HighlightInfo, fileText: String): String = { + val range = info.range + val rangeText = fileText.substring(range.getStartOffset, range.getEndOffset) + s"$range[$rangeText]: ${info.getDescription}" + } + + def checkTextHasError(text: String, allowAdditionalHighlights: Boolean = false): Unit = { + val expectedHighlights = configureByText(text) + val actualHighlights = findMatchingHighlights(text) + assertTextHasError(expectedHighlights, actualHighlights, allowAdditionalHighlights) + } + + def assertTextHasError( + expectedHighlights: Seq[ExpectedHighlight], + actualHighlights: Seq[HighlightInfo], + allowAdditionalHighlights: Boolean, + ): Unit = { + val expectedHighlightRanges = expectedHighlights.map(_.range) + val actualHighlightRanges = actualHighlights.map(_.range) + + val expectedRangesNotFound = expectedHighlightRanges.filterNot(actualHighlightRanges.contains) + if (shouldPass: @nowarn("cat=deprecation")) { + assertTrue( + s"Highlights not found: $description", + actualHighlightRanges.nonEmpty + ) + assertTrue( + s"""Highlights found at: ${actualHighlightRanges.mkString(", ")} + |not found: ${expectedRangesNotFound.mkString(", ")}""".stripMargin, + expectedRangesNotFound.isEmpty + ) + + val fileText = getFile.getText + assertNoDuplicates(actualHighlights, fileText) + + if (!allowAdditionalHighlights) { + assertTrue( + s"""Found too many highlights: + |${highlightsDebugText(actualHighlights, fileText)} + |expected: ${expectedHighlightRanges.mkString(", ")}""".stripMargin, + actualHighlightRanges.length == expectedHighlightRanges.length + ) + } + } else { + assertTrue(failingPassed, actualHighlightRanges.isEmpty) + assertTrue(failingPassed, expectedRangesNotFound.nonEmpty) + } + } + + private def assertNoDuplicates(highlights: Seq[HighlightInfo], fileText: String): Unit = { + val duplicatedHighlights = highlights + .groupBy(_.range).toSeq + .collect { case (_, highlights) if highlights.size > 1 => highlights } + .flatten + assertTrue( + s"Some highlights were duplicated:\n${highlightsDebugText(duplicatedHighlights, fileText: String)}", + duplicatedHighlights.isEmpty + ) + } + + def configureByText(text: String): Seq[ExpectedHighlight] = { + val fileTextNormalized = + createTestText(text).withNormalizedSeparator.trim + + if (isScratchFile) { + val vFile = createScratchFile(fileTextNormalized) + baseFixture.configureFromExistingVirtualFile(vFile) + } else { + baseFixture.configureByText(fileType, fileTextNormalized) + } + + val (_, expectedRanges) = MarkersUtils.extractMarker(fileTextNormalized, START, END, caretMarker = Some(CARET)) + val expectedHighlights = expectedRanges.map(ExpectedHighlight) + + onFileCreated(baseFixture.getFile) + expectedHighlights + } + + /** + * @param text the original text is only used to check if there is an explicit caret marker inside it. + * If there is a caret marker, only highlightings at caret are checked. + */ + def findMatchingHighlights(text: String): Seq[HighlightInfo] = { + val caretOffset = if (text.contains(CARET)) Some(getEditor.getCaretModel.getOffset) else None + findMatchingHighlights(caretOffset) + } + + def findMatchingHighlights(caretOffset: Option[Int] = None): Seq[HighlightInfo] = { + val highlightsAll = baseFixture.doHighlighting().asScala.toSeq + val highlightsMatchingDescription = highlightsAll.filter(highlightInfo => { + val description = highlightInfo.getDescription + description != null && descriptionMatcher(description) + }) + val highlightsInRange = highlightsMatchingDescription.filter(checkOffset(_, caretOffset)) + highlightsInRange + } + + private def createScratchFile(normalizedText: String) = { + val fileName = s"aaa.${fileType.getDefaultExtension}" + val language = fileType.getLanguage + ScratchRootType.getInstance.createScratchFile(getProject, fileName, language, normalizedText) + } +} + +object ScalaQuickFixTestFixture { + case class ExpectedHighlight(range: TextRange) + case class TestPrepareResult(expectedHighlights: Seq[ExpectedHighlight], actualHighlights: Seq[HighlightInfo]) + + def findRegisteredQuickFixes(info: HighlightInfo): Seq[IntentionAction] = { + val builder = Seq.newBuilder[IntentionAction] + info.findRegisteredQuickFix { (descriptor, _) => + builder += descriptor.getAction + null + } + builder.result() + } + + private def highlightedRange(info: HighlightInfo): TextRange = + new TextRange(info.getStartOffset, info.getEndOffset) + + private def checkOffset(highlightInfo: HighlightInfo, caretOffset: Option[Int]): Boolean = { + caretOffset.forall { offset => + val range = highlightedRange(highlightInfo) + range.containsOffset(offset) + } + } +} diff --git a/src/test/scala/zio/inspections/ProvideMacroInspectionTest.scala b/src/test/scala/zio/inspections/ProvideMacroInspectionTest.scala index f61ffcf6..faad0039 100644 --- a/src/test/scala/zio/inspections/ProvideMacroInspectionTest.scala +++ b/src/test/scala/zio/inspections/ProvideMacroInspectionTest.scala @@ -1,6 +1,6 @@ package zio.inspections -import intellij.testfixtures.RichStr +import org.jetbrains.plugins.scala.DependencyManagerBase._ import org.jetbrains.plugins.scala.base.libraryLoaders.{IvyManagedLoader, LibraryLoader} import zio.intellij.inspections.macros.{ErrorRendering, ProvideMacroInspection} diff --git a/src/test/scala/zio/inspections/SimplifyAssertInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyAssertInspectionTest.scala index 4f145ddd..a3481913 100644 --- a/src/test/scala/zio/inspections/SimplifyAssertInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyAssertInspectionTest.scala @@ -45,16 +45,16 @@ class SimplifyEqualToTypeTest extends SimplifyAssertInspectionTest { test("assert(a)(isNull)", "assertTrue(a == null)") def test_isTrue() = - test("assert(List(1,2,3).isEmpty)(isTrue)", "assertTrue(List(1,2,3).isEmpty)") + test("assert(List(1,2,3).isEmpty)(isTrue)", "assertTrue(List(1, 2, 3).isEmpty)") def test_isFalse() = - test("assert(List(1,2,3).isEmpty)(isFalse)", "assertTrue(!(List(1,2,3).isEmpty))") + test("assert(List(1,2,3).isEmpty)(isFalse)", "assertTrue(!(List(1, 2, 3).isEmpty))") def test_contains_non_iterable() = z(s"""${START}assert("abc")(contains("a"))$END""").assertNotHighlighted() def test_contains_iterable() = - test("assert(List(1,2,3))(contains(2))", "assertTrue(List(1,2,3).contains(2))") + test("assert(List(1,2,3))(contains(2))", "assertTrue(List(1, 2, 3).contains(2))") def test_containsString_non_string() = z(s"""${START}assert(List("a"))(containsString("a"))$END""").assertNotHighlighted() @@ -66,19 +66,19 @@ class SimplifyEqualToTypeTest extends SimplifyAssertInspectionTest { z(s"""${START}assert("abc")(exists(equalTo("a")))$END""").assertNotHighlighted() def test_exists_anything() = - test("assert(List(1,2,3))(exists(anything))", "assertTrue(List(1,2,3).nonEmpty)") + test("assert(List(1,2,3))(exists(anything))", "assertTrue(List(1, 2, 3).nonEmpty)") def test_exists_equalTo() = - test("assert(List(1,2,3))(exists(equalTo(2)))", "assertTrue(List(1,2,3).contains(2))") + test("assert(List(1,2,3))(exists(equalTo(2)))", "assertTrue(List(1, 2, 3).contains(2))") def test_exists_expr() = - test("assert(List(1,2,3))(exists(isGreaterThanEqualTo(2)))", "assertTrue(List(1,2,3).exists(_ >= 2))") + test("assert(List(1,2,3))(exists(isGreaterThanEqualTo(2)))", "assertTrue(List(1, 2, 3).exists(_ >= 2))") def test_startsWith_non_seq() = z(s"""${START}assert("abc")(startsWith("ab"))$END""").assertNotHighlighted() def test_startsWith() = - test("assert(List(1,2,3))(startsWith(List(1,2)))", "assertTrue(List(1,2,3).startsWith(List(1,2)))") + test("assert(List(1,2,3))(startsWith(List(1,2)))", "assertTrue(List(1, 2, 3).startsWith(List(1, 2)))") def test_startsWithString_non_string() = z(s"""${START}assert(List("abc"))(startsWithString(List("ab")))$END""").assertNotHighlighted() @@ -105,7 +105,7 @@ class SimplifyEqualToTypeTest extends SimplifyAssertInspectionTest { test("""assert(1)(isPositive)""", """assertTrue(1 > 0)""") def test_isNonEmpty() = - test("""assert(List(1,3))(isNonEmpty)""", """assertTrue(List(1,3).nonEmpty)""") + test("""assert(List(1,3))(isNonEmpty)""", """assertTrue(List(1, 3).nonEmpty)""") def test_isNaNDouble() = test("""assert(1.0d)(isNaNDouble)""", """assertTrue(1.0d.isNaN)""") diff --git a/src/test/scala/zio/inspections/SimplifyErrorModificationInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyErrorModificationInspectionTest.scala index e28c43aa..d885b81f 100644 --- a/src/test/scala/zio/inspections/SimplifyErrorModificationInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyErrorModificationInspectionTest.scala @@ -44,10 +44,12 @@ abstract class SimplifyErrorModificationInspectionTest(toReplace: String, toRepl def testDefReplacement(): Unit = { val text = z { s"""def foo = ZIO.succeed(1) + | |foo.$methodToReplace""".stripMargin } val result = z { s"""def foo = ZIO.succeed(1) + | |foo.$methodToReplaceWith""".stripMargin } testQuickFix(text, result, hint) diff --git a/src/test/scala/zio/inspections/SimplifyErrorRecoveryInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyErrorRecoveryInspectionTest.scala index 9afb35cf..3d7241ce 100644 --- a/src/test/scala/zio/inspections/SimplifyErrorRecoveryInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyErrorRecoveryInspectionTest.scala @@ -49,6 +49,7 @@ abstract class SimplifyErrorRecoveryInspectionTest(toReplace: String, toReplaceW } val result = z { s"""def foo = UIO(1) + | |foo.$methodToReplaceWith""".stripMargin } testQuickFix(text, result, hint) diff --git a/src/test/scala/zio/inspections/SimplifyErrorSeparationInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyErrorSeparationInspectionTest.scala index a277b6e8..f06834d0 100644 --- a/src/test/scala/zio/inspections/SimplifyErrorSeparationInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyErrorSeparationInspectionTest.scala @@ -15,6 +15,7 @@ abstract class SimplifyErrorSeparationInspectionTest(toReplace: String, toReplac private def zdef(s: String): String = z { s""" |def foo(el: Any) = UIO(el) + | |$s |""".stripMargin } @@ -22,6 +23,7 @@ abstract class SimplifyErrorSeparationInspectionTest(toReplace: String, toReplac private def zval(s: String): String = z { s""" |val foo: Any => UIO[Any] = el => UIO(el) + | |$s |""".stripMargin } diff --git a/src/test/scala/zio/inspections/SimplifyForeachInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyForeachInspectionTest.scala index 49edd97b..a6b08dc7 100644 --- a/src/test/scala/zio/inspections/SimplifyForeachInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyForeachInspectionTest.scala @@ -35,6 +35,17 @@ abstract class SimplifyForeachInspectionTest[S <: ZInspection: ClassTag]( | } yield () |}""".stripMargin + // same as above, but padded with extra spacing because tests do a string comparison... + private val zioForBlockMethodToReplaceWith = + s"""ZIO.$methodToReplaceWith$nParamList(myIterable) { + | it => + | println(it) + | for { + | _ <- ZIO.fail(???) + | } yield () + | }""".stripMargin + + override protected val hint: String = s"Replace with ZIO.$methodToReplaceWith" override protected def isZIO1: Boolean = isZIO1Test @@ -81,7 +92,7 @@ abstract class SimplifyForeachInspectionTest[S <: ZInspection: ClassTag]( val result = z { s"""val myIterable: Iterable[String] = ??? |for { - | _ <- $zioBlockMethodToReplaceWith + | _ <- $zioForBlockMethodToReplaceWith |} yield ???""".stripMargin } testQuickFix(text, result, hint) @@ -152,7 +163,7 @@ abstract class SimplifyForeachInspectionTest[S <: ZInspection: ClassTag]( | _ <- b | _ <- $zioMethodToReplaceWith | _ <- b - | _ <- $zioBlockMethodToReplaceWith + | _ <- $zioForBlockMethodToReplaceWith | _ <- b |} yield ???""".stripMargin } diff --git a/src/test/scala/zio/inspections/SimplifyMapBothInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyMapBothInspectionTest.scala index ae8543c2..d695768c 100644 --- a/src/test/scala/zio/inspections/SimplifyMapBothInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyMapBothInspectionTest.scala @@ -43,12 +43,12 @@ class SimplifyMapBothInspectionTest extends ZSimplifyInspectionTest[SimplifyMapB | b | b | b - | }, { + |}, { | a => | a | a | a - | })""".stripMargin + |})""".stripMargin } testQuickFix(text, result, hint) } @@ -86,10 +86,10 @@ class SimplifyMapBothInspectionTest extends ZSimplifyInspectionTest[SimplifyMapB val result = z { """ZIO.succeed(42).mapBoth({ | _ => { - | b - | b - | b - | } + | b + | b + | b + | } |}, { | a => | a @@ -185,10 +185,10 @@ class SimplifyMapBothInspectionTest extends ZSimplifyInspectionTest[SimplifyMapB | } |}, { | _ => { - | a - | a - | a - | } + | a + | a + | a + | } |})""".stripMargin } testQuickFix(text, result, hint) @@ -275,10 +275,10 @@ class SimplifyMapBothInspectionTest extends ZSimplifyInspectionTest[SimplifyMapB | a |}, { | _ => { - | b - | b - | b - | } + | b + | b + | b + | } |})""".stripMargin } testQuickFix(text, result, hint) @@ -316,7 +316,7 @@ class SimplifyMapBothInspectionTest extends ZSimplifyInspectionTest[SimplifyMapB } val result = z { """ZIO.succeed(42).mapBoth({ - | _ => { + | _ => { | a | a | a @@ -364,16 +364,16 @@ class SimplifyMapBothInspectionTest extends ZSimplifyInspectionTest[SimplifyMapB val result = z { """ZIO.succeed(42).mapBoth({ | _ => { - | a - | a - | a - | } + | a + | a + | a + | } |}, { | _ => { - | b - | b - | b - | } + | b + | b + | b + | } |})""".stripMargin } testQuickFix(text, result, hint) diff --git a/src/test/scala/zio/inspections/SimplifyUnlessInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyUnlessInspectionTest.scala index b23447c0..9a72a513 100644 --- a/src/test/scala/zio/inspections/SimplifyUnlessInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyUnlessInspectionTest.scala @@ -95,6 +95,7 @@ abstract class SimplifyUnlessInspectionTestBase(replaceWith: String, `ZIO.unless def base(expr: String): String = s"""|val a = true |def b(one: Int, two: Int, three: Int) = ZIO.succeed(42) + | |$expr""".stripMargin val methodCall = "b(1, 2, 3)" @@ -169,8 +170,8 @@ class SimplifyUnlessInspectionTest val complexExpr = """for { - | i <- ZIO.succeed(42) - | s <- ZIO.succeed("wrap this") + | i <- ZIO.succeed(42) + | s <- ZIO.succeed("wrap this") |} yield s * i""".stripMargin locally { @@ -220,9 +221,9 @@ class SimplifyZIOUnlessInspectionTest val complexExpr = """for { - | i <- ZIO.succeed(42) - | s <- ZIO.succeed("wrap this") - |} yield s * i""".stripMargin + | i <- ZIO.succeed(42) + | s <- ZIO.succeed("wrap this") + | } yield s * i""".stripMargin locally { z(base(s"${START}if (a) ZIO.unit else $complexExpr$END")).assertHighlighted() diff --git a/src/test/scala/zio/inspections/SimplifyWhenInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyWhenInspectionTest.scala index 636d5f9a..3b347f75 100644 --- a/src/test/scala/zio/inspections/SimplifyWhenInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyWhenInspectionTest.scala @@ -95,6 +95,7 @@ abstract class SimplifyWhenInspectionTestBase(replaceWith: String, `ZIO.when`: S def base(expr: String): String = s"""|val a = true |def b(one: Int, two: Int, three: Int) = ZIO.succeed(42) + | |$expr""".stripMargin val methodCall = "b(1, 2, 3)" @@ -168,8 +169,8 @@ class SimplifyWhenInspectionTest extends SimplifyWhenInspectionTestBase(".when", val complexExpr = """for { - | i <- ZIO.succeed(42) - | s <- ZIO.succeed("wrap this") + | i <- ZIO.succeed(42) + | s <- ZIO.succeed("wrap this") |} yield s * i""".stripMargin locally { @@ -220,9 +221,9 @@ class SimplifyZIOWhenInspectionTest val complexExpr = """for { - | i <- ZIO.succeed(42) - | s <- ZIO.succeed("wrap this") - |} yield s * i""".stripMargin + | i <- ZIO.succeed(42) + | s <- ZIO.succeed("wrap this") + | } yield s * i""".stripMargin locally { z(base(s"${START}if (a) $complexExpr else ZIO.unit$END")).assertHighlighted() diff --git a/src/test/scala/zio/inspections/SimplifyZipLeftInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyZipLeftInspectionTest.scala index 2393e0a7..308ba259 100644 --- a/src/test/scala/zio/inspections/SimplifyZipLeftInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyZipLeftInspectionTest.scala @@ -33,9 +33,9 @@ class SimplifyTapWithZipLeftTest extends ZipLeftInspectionTest(".zipLeft") { } val result = z { """ZIO.succeed("Remedios Varo").zipLeft { - | x - | x - | x + | x + | x + | x |}""".stripMargin } testQuickFix(text, result, hint) diff --git a/src/test/scala/zio/inspections/SimplifyZipRightInspectionTest.scala b/src/test/scala/zio/inspections/SimplifyZipRightInspectionTest.scala index 9f995423..dcd805b6 100644 --- a/src/test/scala/zio/inspections/SimplifyZipRightInspectionTest.scala +++ b/src/test/scala/zio/inspections/SimplifyZipRightInspectionTest.scala @@ -33,9 +33,9 @@ class SimplifyFlatmapWithZipRightTest extends ZipRightInspectionTest(".zipRight" } val result = z { """ZIO.succeed("Remedios Varo").zipRight { - | x - | x - | x + | x + | x + | x |}""".stripMargin } testQuickFix(text, result, hint) diff --git a/src/test/scala/zio/inspections/ZSimplifyInspectionTest.scala b/src/test/scala/zio/inspections/ZSimplifyInspectionTest.scala index 8b3a8bf4..a5e4cbf1 100644 --- a/src/test/scala/zio/inspections/ZSimplifyInspectionTest.scala +++ b/src/test/scala/zio/inspections/ZSimplifyInspectionTest.scala @@ -1,7 +1,8 @@ package zio.inspections import com.intellij.codeInspection.LocalInspectionTool -import intellij.testfixtures._ +import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.plugins.scala.DependencyManagerBase._ import org.jetbrains.plugins.scala.base.libraryLoaders._ import org.jetbrains.plugins.scala.codeInspection.ScalaInspectionTestBase import org.jetbrains.plugins.scala.codeInspection.collections._ @@ -39,8 +40,8 @@ trait ZInspectionTestBase[T <: LocalInspectionTool] { base: ScalaInspectionTestB | def f(a: Any, b: Any): ZIO[Any, Throwable, Unit] = ??? | | def foo = { - | $s - | } + |${s.split("\n").map(l => " " + l).mkString("\n")} + | } |} |trait Logger { | def log[A](a: A): ZIO[Any, Nothing, Unit]