From 55beff87fd4e66b979a16523265664f33eecbde2 Mon Sep 17 00:00:00 2001 From: Alex Henning Johannessen Date: Thu, 22 Feb 2024 15:35:28 +0000 Subject: [PATCH 1/5] compiler: account for Scala 2 and 3 differences in generated vararg code (cherry picked from commit c9bef0a8de2d4a150405535e0fbdc630cc2fcc9e) # Conflicts: # .gitignore # compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java --- .gitignore | 11 +++++ .../japi/twirl/compiler/TwirlCompiler.java | 2 + .../play/twirl/compiler/TwirlCompiler.scala | 48 ++++++++++++++++--- .../play/twirl/compiler/test/Helper.scala | 1 + .../play/twirl/compiler/test/Helper.scala | 12 ++++- .../main/scala/play/twirl/sbt/SbtTwirl.scala | 3 +- .../play/twirl/sbt/TemplateCompiler.scala | 4 +- 7 files changed, 70 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 9e31eead..b40b25b8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,14 @@ project/plugins/project/ .idea .bloop/ + +.vscode/ + +# Metals +.metals/ +.bloop/ +project/metals.sbt +metals.sbt + +# BSP +.bsp/* diff --git a/compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java b/compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java index 9db8ecf9..aeabdf8f 100644 --- a/compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java +++ b/compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java @@ -62,6 +62,7 @@ public static Optional compile( List constructorAnnotations, Codec codec, boolean inclusiveDot) { + String scalaVersion = play.twirl.compiler.BuildInfo$.MODULE$.scalaVersion(); Seq scalaAdditionalImports = JavaConverters$.MODULE$ .asScalaBufferConverter(new ArrayList(additionalImports)) @@ -75,6 +76,7 @@ public static Optional compile( sourceDirectory, generatedDirectory, formatterType, + scalaVersion, scalaAdditionalImports, scalaConstructorAnnotations, codec, diff --git a/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala b/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala index ccac8c90..dc4d276d 100644 --- a/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala +++ b/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala @@ -162,6 +162,17 @@ case class GeneratedSourceVirtual(path: String) extends AbstractGeneratedSource object TwirlCompiler { + // For constants that depends on the Scala 2 or 3 mode. + private[compiler] class ScalaCompat(emitScala3Sources: Boolean) { + val varargSplicesSyntax: String = + if (emitScala3Sources) "*" else ": _*" + } + + private[compiler] object ScalaCompat { + def apply(scalaVersion: String): ScalaCompat = + new ScalaCompat(scalaVersion.startsWith("3.")) + } + def defaultImports(scalaVersion: String) = { val implicits = if (scalaVersion.startsWith("3.")) { Seq( @@ -195,6 +206,7 @@ object TwirlCompiler { sourceDirectory: File, generatedDirectory: File, formatterType: String, + scalaVersion: String, additionalImports: collection.Seq[String] = Nil, constructorAnnotations: collection.Seq[String] = Nil, codec: Codec = TwirlIO.defaultCodec, @@ -211,6 +223,7 @@ object TwirlCompiler { relativePath(source), resultType, formatterType, + scalaVersion, additionalImports, constructorAnnotations, inclusiveDot @@ -228,11 +241,13 @@ object TwirlCompiler { sourceDirectory: File, resultType: String, formatterType: String, + scalaVersion: String, additionalImports: collection.Seq[String] = Nil, constructorAnnotations: collection.Seq[String] = Nil, codec: Codec = TwirlIO.defaultCodec, inclusiveDot: Boolean = false ) = { + val (templateName, generatedSource) = generatedFileVirtual(source, sourceDirectory, inclusiveDot) val generated = parseAndGenerateCode( templateName, @@ -241,6 +256,7 @@ object TwirlCompiler { relativePath(source), resultType, formatterType, + scalaVersion, additionalImports, constructorAnnotations, inclusiveDot @@ -252,13 +268,14 @@ object TwirlCompiler { private def relativePath(file: File): String = new File(".").toURI.relativize(file.toURI).getPath - def parseAndGenerateCode( + private def parseAndGenerateCode( templateName: Array[String], content: Array[Byte], codec: Codec, relativePath: String, resultType: String, formatterType: String, + scalaVersion: String, additionalImports: collection.Seq[String], constructorAnnotations: collection.Seq[String], inclusiveDot: Boolean @@ -274,6 +291,7 @@ object TwirlCompiler { parsed, resultType, formatterType, + ScalaCompat(scalaVersion), additionalImports, constructorAnnotations ) @@ -446,10 +464,12 @@ object TwirlCompiler { root: Template, resultType: String, formatterType: String, + scalaCompat: ScalaCompat, additionalImports: collection.Seq[String], constructorAnnotations: collection.Seq[String] ): collection.Seq[Any] = { - val (renderCall, f, templateType) = TemplateAsFunctionCompiler.getFunctionMapping(root.params.str, resultType) + val (renderCall, f, templateType) = + TemplateAsFunctionCompiler.getFunctionMapping(root.params.str, resultType, scalaCompat) // Get the imports that we need to include, filtering out empty imports val imports: Seq[Any] = Seq(additionalImports.map(i => Seq("import ", i, "\n")), formatImports(root.topImports)) @@ -499,7 +519,7 @@ package """ :+ packageName :+ """ templateImports.map(_.replace("%format%", extension)) } - def generateFinalTemplate( + private[compiler] def generateFinalTemplate( relativePath: String, contents: Array[Byte], packageName: String, @@ -507,11 +527,21 @@ package """ :+ packageName :+ """ root: Template, resultType: String, formatterType: String, + scalaCompat: ScalaCompat, additionalImports: collection.Seq[String], constructorAnnotations: collection.Seq[String] ): String = { val generated = - generateCode(packageName, name, root, resultType, formatterType, additionalImports, constructorAnnotations) + generateCode( + packageName, + name, + root, + resultType, + formatterType, + scalaCompat, + additionalImports, + constructorAnnotations + ) Source.finalSource(relativePath, contents, generated, Hash(contents, additionalImports)) } @@ -531,7 +561,11 @@ package """ :+ packageName :+ """ } } - def getFunctionMapping(signature: String, returnType: String): (String, String, String) = { + private[compiler] def getFunctionMapping( + signature: String, + returnType: String, + sc: ScalaCompat + ): (String, String, String) = { val params: List[List[Term.Param]] = try { @@ -573,7 +607,7 @@ package """ :+ packageName :+ """ .map { p => p.name.toString + Option(p.decltpe.get.toString) .filter(_.endsWith("*")) - .map(_ => ".toIndexedSeq:_*") + .map(_ => s".toIndexedSeq${sc.varargSplicesSyntax}") .getOrElse("") } .mkString(",") + ")" @@ -601,7 +635,7 @@ package """ :+ packageName :+ """ .map { p => p.name.toString + Option(p.decltpe.get.toString) .filter(_.endsWith("*")) - .map(_ => ".toIndexedSeq:_*") + .map(_ => s".toIndexedSeq${sc.varargSplicesSyntax}") .getOrElse("") } .mkString(",") + ")" diff --git a/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala b/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala index 6448bf17..c61b53d0 100644 --- a/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala +++ b/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala @@ -99,6 +99,7 @@ object Helper { sourceDir, generatedDir, "play.twirl.api.HtmlFormat", + scalaVersion, additionalImports = TwirlCompiler.defaultImports(scalaVersion) ++ additionalImports ) diff --git a/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala b/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala index b53fe10b..63d65117 100644 --- a/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala +++ b/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala @@ -60,14 +60,19 @@ object Helper { ): CompiledTemplate[T] = { val scalaVersion = play.twirl.compiler.BuildInfo.scalaVersion val templateFile = new File(sourceDir, templateName) - val Some(generated) = twirlCompiler.compile( + val generatedOpt: Option[File] = twirlCompiler.compile( templateFile, sourceDir, generatedDir, "play.twirl.api.HtmlFormat", + scalaVersion, additionalImports = TwirlCompiler.defaultImports(scalaVersion) ++ additionalImports ) + val generated = generatedOpt.getOrElse { + throw new FileNotFoundException(s"Could not find generated file for $templateName") + } + val mapper = GeneratedSource(generated) val compilerArgs = Array( @@ -94,7 +99,10 @@ object Helper { class TestDriver(outDir: Path, compilerArgs: Array[String], path: Path) extends Driver { def compile(): Reporter = { - val Some((toCompile, rootCtx)) = setup(compilerArgs :+ path.toAbsolutePath.toString, initCtx.fresh) + val setupOpt = setup(compilerArgs :+ path.toAbsolutePath.toString, initCtx.fresh) + val (toCompile, rootCtx) = setupOpt.getOrElse { + throw new Exception("Failed to initialize compiler") + } val silentReporter = new ConsoleReporter.AbstractConsoleReporter { def printMessage(msg: String): Unit = { diff --git a/sbt-twirl/src/main/scala/play/twirl/sbt/SbtTwirl.scala b/sbt-twirl/src/main/scala/play/twirl/sbt/SbtTwirl.scala index 2f341cc5..e3728a1f 100644 --- a/sbt-twirl/src/main/scala/play/twirl/sbt/SbtTwirl.scala +++ b/sbt-twirl/src/main/scala/play/twirl/sbt/SbtTwirl.scala @@ -117,7 +117,8 @@ object SbtTwirl extends AutoPlugin { (compileTemplates / includeFilter).value, (compileTemplates / excludeFilter).value, Codec(sourceEncoding.value), - streams.value.log + streams.value.log, + scalaVersion.value ) } diff --git a/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala b/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala index beae4d51..26155385 100644 --- a/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala +++ b/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala @@ -20,7 +20,8 @@ object TemplateCompiler { includeFilter: FileFilter, excludeFilter: FileFilter, codec: Codec, - log: Logger + log: Logger, + scalaVersion: String ): Seq[File] = { try { syncGenerated(targetDirectory, codec) @@ -32,6 +33,7 @@ object TemplateCompiler { sourceDirectory, targetDirectory, format, + scalaVersion, imports, constructorAnnotations, codec, From c50d2ed878ba88bb73cdbd8a716f3d62ae23b51b Mon Sep 17 00:00:00 2001 From: Alex Henning Johannessen Date: Fri, 23 Feb 2024 09:58:16 +0000 Subject: [PATCH 2/5] fix mima warnings (cherry picked from commit 3190e1ea0cb1110954f47c9d8eaaf019d05ec45e) --- .../japi/twirl/compiler/TwirlCompiler.java | 2 +- .../play/twirl/compiler/TwirlCompiler.scala | 137 ++++++++++++++++-- .../play/twirl/compiler/test/Helper.scala | 8 +- .../play/twirl/compiler/test/Helper.scala | 8 +- .../play/twirl/sbt/TemplateCompiler.scala | 2 +- 5 files changed, 141 insertions(+), 16 deletions(-) diff --git a/compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java b/compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java index aeabdf8f..43dce4c9 100644 --- a/compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java +++ b/compiler/src/main/java/play/japi/twirl/compiler/TwirlCompiler.java @@ -76,7 +76,7 @@ public static Optional compile( sourceDirectory, generatedDirectory, formatterType, - scalaVersion, + scala.Option.apply(scalaVersion), scalaAdditionalImports, scalaConstructorAnnotations, codec, diff --git a/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala b/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala index dc4d276d..c9c3969e 100644 --- a/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala +++ b/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala @@ -162,15 +162,15 @@ case class GeneratedSourceVirtual(path: String) extends AbstractGeneratedSource object TwirlCompiler { - // For constants that depends on the Scala 2 or 3 mode. + // For constants that depend on Scala 2 or 3 mode. private[compiler] class ScalaCompat(emitScala3Sources: Boolean) { val varargSplicesSyntax: String = if (emitScala3Sources) "*" else ": _*" } private[compiler] object ScalaCompat { - def apply(scalaVersion: String): ScalaCompat = - new ScalaCompat(scalaVersion.startsWith("3.")) + def apply(scalaVersion: Option[String]): ScalaCompat = + new ScalaCompat(scalaVersion.exists(_.startsWith("3."))) } def defaultImports(scalaVersion: String) = { @@ -206,12 +206,34 @@ object TwirlCompiler { sourceDirectory: File, generatedDirectory: File, formatterType: String, - scalaVersion: String, additionalImports: collection.Seq[String] = Nil, constructorAnnotations: collection.Seq[String] = Nil, codec: Codec = TwirlIO.defaultCodec, inclusiveDot: Boolean = false - ) = { + ): Option[File] = + compile( + source, + sourceDirectory, + generatedDirectory, + formatterType, + None, + additionalImports, + constructorAnnotations, + codec, + inclusiveDot + ) + + def compile( + source: File, + sourceDirectory: File, + generatedDirectory: File, + formatterType: String, + scalaVersion: Option[String], + additionalImports: collection.Seq[String], + constructorAnnotations: collection.Seq[String], + codec: Codec, + inclusiveDot: Boolean + ): Option[File] = { val resultType = formatterType + ".Appendable" val (templateName, generatedSource) = generatedFile(source, codec, sourceDirectory, generatedDirectory, inclusiveDot) @@ -241,12 +263,36 @@ object TwirlCompiler { sourceDirectory: File, resultType: String, formatterType: String, - scalaVersion: String, additionalImports: collection.Seq[String] = Nil, constructorAnnotations: collection.Seq[String] = Nil, codec: Codec = TwirlIO.defaultCodec, inclusiveDot: Boolean = false - ) = { + ): GeneratedSourceVirtual = + compileVirtual( + content, + source, + sourceDirectory, + resultType, + formatterType, + None, + additionalImports, + constructorAnnotations, + codec, + inclusiveDot + ) + + def compileVirtual( + content: String, + source: File, + sourceDirectory: File, + resultType: String, + formatterType: String, + scalaVersion: Option[String], + additionalImports: collection.Seq[String], + constructorAnnotations: collection.Seq[String], + codec: Codec, + inclusiveDot: Boolean + ): GeneratedSourceVirtual = { val (templateName, generatedSource) = generatedFileVirtual(source, sourceDirectory, inclusiveDot) val generated = parseAndGenerateCode( @@ -268,6 +314,29 @@ object TwirlCompiler { private def relativePath(file: File): String = new File(".").toURI.relativize(file.toURI).getPath + def parseAndGenerateCode( + templateName: Array[String], + content: Array[Byte], + codec: Codec, + relativePath: String, + resultType: String, + formatterType: String, + additionalImports: collection.Seq[String], + constructorAnnotations: collection.Seq[String], + inclusiveDot: Boolean + ): String = parseAndGenerateCode( + templateName, + content, + codec, + relativePath, + resultType, + formatterType, + None, + additionalImports, + constructorAnnotations, + inclusiveDot + ) + private def parseAndGenerateCode( templateName: Array[String], content: Array[Byte], @@ -275,11 +344,11 @@ object TwirlCompiler { relativePath: String, resultType: String, formatterType: String, - scalaVersion: String, + scalaVersion: Option[String], additionalImports: collection.Seq[String], constructorAnnotations: collection.Seq[String], inclusiveDot: Boolean - ) = { + ): String = { val templateParser = new TwirlParser(inclusiveDot) templateParser.parse(new String(content, codec.charSet)) match { case templateParser.Success(parsed: Template, rest) if rest.atEnd() => { @@ -459,6 +528,25 @@ object TwirlCompiler { } def generateCode( + packageName: String, + name: String, + root: Template, + resultType: String, + formatterType: String, + additionalImports: collection.Seq[String], + constructorAnnotations: collection.Seq[String] + ): collection.Seq[Any] = generateCode( + packageName, + name, + root, + resultType, + formatterType, + ScalaCompat(None), + additionalImports, + constructorAnnotations + ) + + private def generateCode( packageName: String, name: String, root: Template, @@ -519,7 +607,30 @@ package """ :+ packageName :+ """ templateImports.map(_.replace("%format%", extension)) } - private[compiler] def generateFinalTemplate( + def generateFinalTemplate( + relativePath: String, + contents: Array[Byte], + packageName: String, + name: String, + root: Template, + resultType: String, + formatterType: String, + additionalImports: collection.Seq[String], + constructorAnnotations: collection.Seq[String] + ): String = generateFinalTemplate( + relativePath, + contents, + packageName, + name, + root, + resultType, + formatterType, + ScalaCompat(None), + additionalImports, + constructorAnnotations + ) + + private def generateFinalTemplate( relativePath: String, contents: Array[Byte], packageName: String, @@ -561,6 +672,12 @@ package """ :+ packageName :+ """ } } + def getFunctionMapping( + signature: String, + returnType: String, + ): (String, String, String) = + getFunctionMapping(signature, returnType, ScalaCompat(None)) + private[compiler] def getFunctionMapping( signature: String, returnType: String, diff --git a/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala b/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala index c61b53d0..afd70e0f 100644 --- a/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala +++ b/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala @@ -6,6 +6,7 @@ package play.twirl.compiler package test import java.io._ +import play.twirl.parser.TwirlIO object Helper { case class CompilationError(message: String, line: Int, column: Int) extends RuntimeException(message) @@ -99,8 +100,11 @@ object Helper { sourceDir, generatedDir, "play.twirl.api.HtmlFormat", - scalaVersion, - additionalImports = TwirlCompiler.defaultImports(scalaVersion) ++ additionalImports + Option(scalaVersion), + additionalImports = TwirlCompiler.defaultImports(scalaVersion) ++ additionalImports, + constructorAnnotations = Nil, + codec = TwirlIO.defaultCodec, + inclusiveDot = false ) val mapper = GeneratedSource(generated) diff --git a/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala b/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala index 63d65117..75c96178 100644 --- a/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala +++ b/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala @@ -20,6 +20,7 @@ import dotty.tools.io.PlainDirectory import dotty.tools.io.Directory import dotty.tools.io.ClassPath import scala.jdk.CollectionConverters._ +import play.twirl.parser.TwirlIO object Helper { case class CompilationError(message: String, line: Int, column: Int) extends RuntimeException(message) @@ -65,8 +66,11 @@ object Helper { sourceDir, generatedDir, "play.twirl.api.HtmlFormat", - scalaVersion, - additionalImports = TwirlCompiler.defaultImports(scalaVersion) ++ additionalImports + Option(scalaVersion), + additionalImports = TwirlCompiler.defaultImports(scalaVersion) ++ additionalImports, + constructorAnnotations = Nil, + codec = TwirlIO.defaultCodec, + inclusiveDot = false ) val generated = generatedOpt.getOrElse { diff --git a/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala b/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala index 26155385..467d34ca 100644 --- a/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala +++ b/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala @@ -33,7 +33,7 @@ object TemplateCompiler { sourceDirectory, targetDirectory, format, - scalaVersion, + Some(scalaVersion), imports, constructorAnnotations, codec, From 9f024c36cb0d58fa04b01e4c5a9b3af557d7d842 Mon Sep 17 00:00:00 2001 From: Alex Henning Johannessen Date: Fri, 23 Feb 2024 11:20:02 +0000 Subject: [PATCH 3/5] add some tests (cherry picked from commit 7fdb5054719a14b9e023515c7e7e8b1c2b2be500) --- .../play/twirl/compiler/test/Helper.scala | 2 +- .../play/twirl/compiler/test/Helper.scala | 2 +- .../twirl/compiler/test/CompilerSpec.scala | 28 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala b/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala index afd70e0f..e94f9da2 100644 --- a/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala +++ b/compiler/src/test/scala-2/play/twirl/compiler/test/Helper.scala @@ -11,7 +11,7 @@ import play.twirl.parser.TwirlIO object Helper { case class CompilationError(message: String, line: Int, column: Int) extends RuntimeException(message) - class CompilerHelper(sourceDir: File, generatedDir: File, generatedClasses: File) { + class CompilerHelper(sourceDir: File, val generatedDir: File, generatedClasses: File) { import java.net._ import scala.collection.mutable import scala.reflect.internal.util.Position diff --git a/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala b/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala index 75c96178..c586d253 100644 --- a/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala +++ b/compiler/src/test/scala-3/play/twirl/compiler/test/Helper.scala @@ -25,7 +25,7 @@ import play.twirl.parser.TwirlIO object Helper { case class CompilationError(message: String, line: Int, column: Int) extends RuntimeException(message) - class CompilerHelper(sourceDir: File, generatedDir: File, generatedClasses: File) { + class CompilerHelper(sourceDir: File, val generatedDir: File, generatedClasses: File) { import java.net._ import scala.collection.mutable diff --git a/compiler/src/test/scala/play/twirl/compiler/test/CompilerSpec.scala b/compiler/src/test/scala/play/twirl/compiler/test/CompilerSpec.scala index 075af74c..909936d1 100644 --- a/compiler/src/test/scala/play/twirl/compiler/test/CompilerSpec.scala +++ b/compiler/src/test/scala/play/twirl/compiler/test/CompilerSpec.scala @@ -10,6 +10,8 @@ import play.twirl.api.Html import play.twirl.parser.TwirlIO import org.scalatest.matchers.must.Matchers import org.scalatest.wordspec.AnyWordSpec +import TwirlCompiler.ScalaCompat +import scala.io.Source class CompilerSpec extends AnyWordSpec with Matchers { @@ -159,6 +161,12 @@ class CompilerSpec extends AnyWordSpec with Matchers { .static(Array(List(1, 2, 3), List(4, 5, 6))) .toString .trim + + val compat = ScalaCompat(Option(BuildInfo.scalaVersion)) + val generatedFile = helper.generatedDir.toPath.resolve("html/varArgsExistential.template.scala").toFile + val generatedText = Source.fromFile(generatedFile).getLines().mkString("\n") + + generatedText must include(s"list.toIndexedSeq${compat.varargSplicesSyntax}") text must be("123456") } @@ -329,4 +337,24 @@ class CompilerSpec extends AnyWordSpec with Matchers { ) hello.static(args).toString.trim must be("the-key => the-value") } + + "ScalaCompat" should { + + val cases = List( + None -> ": _*", + Some("2.12.18") -> ": _*", + Some("2.13.12") -> ": _*", + Some("3.3.1") -> "*" + ) + + "produce correct varargs splice syntax" in { + + cases.foreach { case (version, expected) => + ScalaCompat(version).varargSplicesSyntax must be(expected) + } + + } + + } + } From 726c1b616748aad09ec973d8c391f9cc00d0f5a7 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Tue, 27 Feb 2024 11:33:23 +0100 Subject: [PATCH 4/5] Avoid duplicates in gitignore (cherry picked from commit ce5e0f188d079382764c92f11f4ea31812d004fb) --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index b40b25b8..5f6153ae 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,6 @@ project/plugins/project/ .scala_dependencies .idea -.bloop/ - .vscode/ # Metals @@ -23,6 +21,3 @@ project/plugins/project/ .bloop/ project/metals.sbt metals.sbt - -# BSP -.bsp/* From e748f9a464d30ede3c6518d092a41f12107748bc Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Tue, 27 Feb 2024 11:53:36 +0100 Subject: [PATCH 5/5] Compatibility (cherry picked from commit c38c2e93d1f6bdaeb3995553c609ebb9feb2a6a2) --- .../play/twirl/sbt/TemplateCompiler.scala | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala b/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala index 467d34ca..4dfb9ed6 100644 --- a/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala +++ b/sbt-twirl/src/main/scala/play/twirl/sbt/TemplateCompiler.scala @@ -11,6 +11,29 @@ import sbt.internal.inc.LoggedReporter object TemplateCompiler { + def compile( + sourceDirectories: Seq[File], + targetDirectory: File, + templateFormats: Map[String, String], + templateImports: Seq[String], + constructorAnnotations: Seq[String], + includeFilter: FileFilter, + excludeFilter: FileFilter, + codec: Codec, + log: Logger + ): Seq[File] = compile( + sourceDirectories, + targetDirectory, + templateFormats, + templateImports, + constructorAnnotations, + includeFilter, + excludeFilter, + codec, + log, + "2.13.x" // using a dummy scala version (not starting with "3." to generate Scala 2 code by default) + ) + def compile( sourceDirectories: Seq[File], targetDirectory: File,