From 1e10509cd3b832bb086564aa24df5e1338e810da Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Tue, 24 Sep 2024 18:22:42 +0200 Subject: [PATCH] Allow to compile with Scala Native Adds Regexes for scala native, splits up uses of certain Collections, System.console, and metaconfig. Also moves some tests to a JVM only directory. --- .github/workflows/ci.yml | 27 ++- build.sbt | 100 ++++---- docs/contributing-scalafmt.md | 2 +- project/Dependencies.scala | 5 +- project/plugins.sbt | 8 +- .../org/scalafmt/cli/CliOptionsUtils.scala | 8 + .../scala/org/scalafmt/cli/CliUtils.scala | 35 +++ .../scala/org/scalafmt/cli/TermUtils.scala | 13 + .../org/scalafmt/cli/CliOptionsUtils.scala | 7 + .../scala/org/scalafmt/cli/CliUtils.scala | 7 + .../scala/org/scalafmt/cli/TermUtils.scala | 9 + .../src/main/scala/org/scalafmt/cli/Cli.scala | 35 +-- .../scala/org/scalafmt/cli/CliOptions.scala | 16 +- .../org/scalafmt/cli/ScalafmtCoreRunner.scala | 2 +- .../scalafmt/cli/ScalafmtDynamicRunner.scala | 2 +- .../scala/org/scalafmt/cli/TermDisplay.scala | 11 +- .../org/scalafmt/config/PlatformConfig.scala | 7 + .../org/scalafmt/config/PlatformConfig.scala | 5 + .../org/scalafmt/config/PlatformConfig.scala | 15 ++ .../org/scalafmt/config/ConfParsed.scala | 2 +- .../org/scalafmt/internal/RegexCompat.scala | 222 ++++++++++++++++++ .../org/scalafmt/CompatCollections.scala | 10 + .../org/scalafmt/CompatCollections.scala | 12 + .../org/scalafmt/sysops/PlatformCompat.scala | 5 + .../org/scalafmt/CompatCollections.scala | 11 + .../org/scalafmt/CompatCollections.scala | 14 ++ .../org/scalafmt/sysops/PlatformCompat.scala | 5 + .../org/scalafmt/CompatCollections.scala | 6 - .../org/scalafmt/CompatCollections.scala | 6 - .../scala/org/scalafmt/sysops/FileOps.scala | 12 +- .../org/scalafmt/community/TestHelpers.scala | 4 +- .../scala/org/scalafmt/ScalafmtProps.scala | 2 +- .../org/scalafmt/cli/CliOptionsJVMTest.scala | 25 ++ .../org/scalafmt/sysops/FileOpsJVMTest.scala | 14 ++ .../org/scalafmt/cli/CliOptionsTest.scala | 15 -- .../test/scala/org/scalafmt/cli/CliTest.scala | 3 +- .../org/scalafmt/sysops/FileOpsTest.scala | 11 - 37 files changed, 550 insertions(+), 143 deletions(-) create mode 100644 scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliOptionsUtils.scala create mode 100644 scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliUtils.scala create mode 100644 scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/TermUtils.scala create mode 100644 scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliOptionsUtils.scala create mode 100644 scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliUtils.scala create mode 100644 scalafmt-cli/native/src/main/scala/org/scalafmt/cli/TermUtils.scala create mode 100644 scalafmt-config/native/src/main/scala/org/scalafmt/config/PlatformConfig.scala create mode 100644 scalafmt-core/native/src/main/scala/org/scalafmt/internal/RegexCompat.scala create mode 100644 scalafmt-sysops/jvm/src/main/scala-2.12/org/scalafmt/CompatCollections.scala create mode 100644 scalafmt-sysops/jvm/src/main/scala-2.13/org/scalafmt/CompatCollections.scala create mode 100644 scalafmt-sysops/jvm/src/main/scala/org/scalafmt/sysops/PlatformCompat.scala create mode 100644 scalafmt-sysops/native/src/main/scala-2.12/org/scalafmt/CompatCollections.scala create mode 100644 scalafmt-sysops/native/src/main/scala-2.13/org/scalafmt/CompatCollections.scala create mode 100644 scalafmt-sysops/native/src/main/scala/org/scalafmt/sysops/PlatformCompat.scala delete mode 100644 scalafmt-sysops/shared/src/main/scala-2.12/org/scalafmt/CompatCollections.scala delete mode 100644 scalafmt-sysops/shared/src/main/scala-2.13/org/scalafmt/CompatCollections.scala rename scalafmt-tests/{shared => jvm}/src/test/scala/org/scalafmt/ScalafmtProps.scala (99%) create mode 100644 scalafmt-tests/jvm/src/test/scala/org/scalafmt/cli/CliOptionsJVMTest.scala create mode 100644 scalafmt-tests/jvm/src/test/scala/org/scalafmt/sysops/FileOpsJVMTest.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53e1db7d24..3d084df099 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,32 @@ jobs: - run: # for GitOps tests git config --global user.email "scalafmt@scalameta.org" && git config --global user.name "scalafmt" - - run: TEST="2.12" sbt ci-test + - run: TEST="2.12" sbt ci-test-jvm shell: bash - - run: TEST="2.13" sbt ci-test + - run: TEST="2.13" sbt ci-test-jvm + shell: bash + test-scala-native: + strategy: + fail-fast: false + matrix: + os: [windows-latest, ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up JVM + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'sbt' + - run: + # for GitOps tests + git config --global user.email "scalafmt@scalameta.org" && git config --global user.name "scalafmt" + - run: TEST="2.12" sbt ci-test-native + shell: bash + - run: TEST="2.13" sbt ci-test-native shell: bash community-test: strategy: diff --git a/build.sbt b/build.sbt index ad7224be98..4d999a1374 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,7 @@ import java.nio.file.Paths +import scala.scalanative.build._ + import Dependencies._ import sbtcrossproject.CrossPlugin.autoImport.crossProject @@ -32,8 +34,6 @@ inThisBuild(List( crossScalaVersions := List(scala213, scala212), resolvers ++= Resolver.sonatypeOssRepos("releases"), resolvers ++= Resolver.sonatypeOssRepos("snapshots"), - libraryDependencies ++= - List(munit.value % Test, scalacheck % Test, scalametaTestkit % Test), testFrameworks += new TestFramework("munit.Framework"), )) @@ -41,8 +41,9 @@ name := "scalafmtRoot" publish / skip := true addCommandAlias("native-image", "cli/nativeImage") +addCommandAlias("scala-native", "cliNative/compile;cliNative/nativeLink") -commands += Command.command("ci-test") { s => +commands += Command.command("ci-test-jvm") { s => val scalaVersion = sys.env.get("TEST") match { case Some("2.12") => scala212 case _ => scala213 @@ -52,8 +53,16 @@ commands += Command.command("ci-test") { s => docsTest :: s } -lazy val dynamic = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) - .in(file("scalafmt-dynamic")).settings( +commands += Command.command("ci-test-native") { s => + val scalaVersion = sys.env.get("TEST") match { + case Some("2.12") => scala212 + case _ => scala213 + } + s"++$scalaVersion" :: "testsNative/test" :: s +} + +lazy val dynamic = crossProject(JVMPlatform, NativePlatform) + .withoutSuffixFor(JVMPlatform).in(file("scalafmt-dynamic")).settings( moduleName := "scalafmt-dynamic", description := "Implementation of scalafmt-interfaces", buildInfoSettings, @@ -63,14 +72,14 @@ lazy val dynamic = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) "io.get-coursier" % "interface" % "0.0.17", "com.typesafe" % "config" % "1.4.3", munit.value % Test, - scalametaTestkit % Test, + scalametaTestkit.value % Test, ), scalacOptions ++= scalacJvmOptions.value, ).dependsOn(interfaces, sysops).dependsOn(core % "test") .enablePlugins(BuildInfoPlugin) -lazy val interfaces = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) - .in(file("scalafmt-interfaces")).settings( +lazy val interfaces = crossProject(JVMPlatform, NativePlatform) + .withoutSuffixFor(JVMPlatform).in(file("scalafmt-interfaces")).settings( moduleName := "scalafmt-interfaces", description := "Dependency-free, pure Java public interfaces to integrate with Scalafmt through a build tool or editor plugin.", @@ -86,8 +95,8 @@ lazy val interfaces = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) }, ) -lazy val sysops = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) - .in(file("scalafmt-sysops")).settings( +lazy val sysops = crossProject(JVMPlatform, NativePlatform) + .withoutSuffixFor(JVMPlatform).in(file("scalafmt-sysops")).settings( moduleName := "scalafmt-sysops", description := "Scalafmt systems operations", scalacOptions ++= scalacJvmOptions.value, @@ -102,45 +111,48 @@ lazy val sysops = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) }, ) -lazy val config = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) - .in(file("scalafmt-config")).settings( +lazy val config = crossProject(JVMPlatform, NativePlatform) + .withoutSuffixFor(JVMPlatform).in(file("scalafmt-config")).settings( moduleName := "scalafmt-config", description := "Scalafmt config parsing", scalacOptions ++= scalacJvmOptions.value, libraryDependencies ++= Seq(metaconfig.value), ).jvmSettings(libraryDependencies ++= Seq(metaconfigTypesafe.value)) + .nativeSettings(libraryDependencies ++= Seq(metaconfigSconfig.value)) // .jsSettings( // libraryDependencies ++= Seq( // metaconfigHocon.value, // ) // ) -lazy val core = crossProject(JVMPlatform).in(file("scalafmt-core")).settings( - moduleName := "scalafmt-core", - buildInfoSettings, - scalacOptions ++= scalacJvmOptions.value, - libraryDependencies ++= Seq("org.scalameta" %% "mdoc-parser" % mdocV), - libraryDependencies ++= { - CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, 13)) => Seq() - case _ => Seq(compilerPlugin( - "org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full, - )) - } - }, -) +lazy val core = crossProject(JVMPlatform, NativePlatform) + .in(file("scalafmt-core")).settings( + moduleName := "scalafmt-core", + buildInfoSettings, + scalacOptions ++= scalacJvmOptions.value, + libraryDependencies ++= Seq("org.scalameta" %%% "mdoc-parser" % mdocV), + libraryDependencies ++= { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 13)) => Seq() + case _ => Seq(compilerPlugin( + "org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full, + )) + } + }, + ) // .jsSettings( // libraryDependencies ++= List( // scalatest.value % Test // must be here for coreJS/test to run anything // ) // ) + .nativeSettings(libraryDependencies += "com.lihaoyi" %%% "fastparse" % "3.1.1") .jvmSettings(Test / run / fork := true).dependsOn(sysops, config, macros) .enablePlugins(BuildInfoPlugin) lazy val coreJVM = core.jvm // lazy val coreJS = core.js -lazy val macros = crossProject(JVMPlatform).in(file("scalafmt-macros")) - .settings( +lazy val macros = crossProject(JVMPlatform, NativePlatform) + .in(file("scalafmt-macros")).settings( moduleName := "scalafmt-macros", buildInfoSettings, scalacOptions ++= scalacJvmOptions.value, @@ -165,8 +177,8 @@ val scalacJvmOptions = Def.setting { cross ++ unused } -lazy val cli = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) - .in(file("scalafmt-cli")).settings( +lazy val cli = crossProject(JVMPlatform, NativePlatform) + .withoutSuffixFor(JVMPlatform).in(file("scalafmt-cli")).settings( moduleName := "scalafmt-cli", assembly / mainClass := Some("org.scalafmt.cli.Cli"), assembly / assemblyOption := (assembly / assemblyOption).value @@ -179,9 +191,9 @@ lazy val cli = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) oldStrategy(x) }, libraryDependencies ++= Seq( - "org.scalameta" %% "munit-diff" % "1.0.2", + "org.scalameta" %%% "munit-diff" % "1.0.2", "com.martiansoftware" % "nailgun-server" % "0.9.1", - "com.github.scopt" %% "scopt" % "4.1.0", + "com.github.scopt" %%% "scopt" % "4.1.0", ), scalacOptions ++= scalacJvmOptions.value, Compile / mainClass := Some("org.scalafmt.cli.Cli"), @@ -195,33 +207,35 @@ lazy val cli = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) val isStatic = sys.env.get("NATIVE_IMAGE_STATIC").exists(_.toBoolean) if (isStatic) Seq("--static") else Nil }, - ).dependsOn(core, dynamic).enablePlugins(NativeImagePlugin) + ).nativeSettings(scalaNativeConfig).dependsOn(core, dynamic) + .enablePlugins(NativeImagePlugin) -lazy val tests = crossProject(JVMPlatform).withoutSuffixFor(JVMPlatform) - .in(file("scalafmt-tests")).settings( +lazy val tests = crossProject(JVMPlatform, NativePlatform) + .withoutSuffixFor(JVMPlatform).in(file("scalafmt-tests")).settings( publish / skip := true, libraryDependencies ++= Seq( // Test dependencies - "com.lihaoyi" %% "scalatags" % "0.13.1", - scalametaTestkit, + "com.lihaoyi" %%% "scalatags" % "0.13.1", + scalametaTestkit.value, munit.value, ), scalacOptions ++= scalacJvmOptions.value, - javaOptions += "-Dfile.encoding=UTF8", buildInfoPackage := "org.scalafmt.tests", buildInfoKeys := Seq[BuildInfoKey]("resourceDirectory" -> { val sharedTests = (baseDirectory.value.getParentFile / "shared").toPath (Test / resourceDirectories).value.find(_.toPath.startsWith(sharedTests)) .get }), - ).enablePlugins(BuildInfoPlugin).dependsOn(core, dynamic, cli) + ).enablePlugins(BuildInfoPlugin) + .jvmSettings(javaOptions += "-Dfile.encoding=UTF8") + .dependsOn(core, dynamic, cli) lazy val communityTests = project.in(file("scalafmt-tests-community")).settings( publish / skip := true, libraryDependencies ++= Seq( // Test dependencies - "com.lihaoyi" %% "scalatags" % "0.13.1", - scalametaTestkit, + "com.lihaoyi" %%% "scalatags" % "0.13.1", + scalametaTestkit.value, munit.value, ), scalacOptions ++= scalacJvmOptions.value, @@ -232,7 +246,7 @@ lazy val communityTests = project.in(file("scalafmt-tests-community")).settings( lazy val benchmarks = project.in(file("scalafmt-benchmarks")).settings( publish / skip := true, moduleName := "scalafmt-benchmarks", - libraryDependencies ++= Seq(scalametaTestkit), + libraryDependencies ++= Seq(scalametaTestkit.value), run / javaOptions ++= Seq( "-Djava.net.preferIPv4Stack=true", "-XX:+AggressiveOpts", @@ -284,3 +298,5 @@ lazy val buildInfoSettings: Seq[Def.Setting[_]] = Seq( buildInfoPackage := "org.scalafmt", buildInfoObject := "Versions", ) + +lazy val scalaNativeConfig = nativeConfig ~= { _.withMode(Mode.releaseFull) } diff --git a/docs/contributing-scalafmt.md b/docs/contributing-scalafmt.md index 3eaf733247..1668390168 100644 --- a/docs/contributing-scalafmt.md +++ b/docs/contributing-scalafmt.md @@ -80,7 +80,7 @@ To build a native image of the command-line interface using - From the project root directory, - run `sbt cli/assembly` - - run `java -jar scalafmt-cli/target/scala-2.13/scalafmt.jar`, to execute recently built artifacts + - run `java -jar scalafmt-cli/jvm/target/scala-2.13/scalafmt.jar`, to execute recently built artifacts ## Random stuff diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 64fcd273fa..6e641fad49 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -21,7 +21,7 @@ object Dependencies { ) } - val scalametaTestkit = "org.scalameta" %% "testkit" % scalametaV + val scalametaTestkit = Def.setting("org.scalameta" %%% "testkit" % scalametaV) val scalacheck = "org.scalacheck" %% "scalacheck" % scalacheckV val munit = Def.setting("org.scalameta" %%% "munit" % munitV) @@ -31,6 +31,7 @@ object Dependencies { val metaconfig = Def.setting("org.scalameta" %%% "metaconfig-core" % metaconfigV) val metaconfigTypesafe = Def .setting("org.scalameta" %%% "metaconfig-typesafe-config" % metaconfigV) - val metaconfigHocon = Def.setting("com.geirsson" %%% "metaconfig-hocon" % metaconfigV) + val metaconfigHocon = Def.setting("com.geirsson" %%% "metaconfig-hocon" % metaconfigV) + val metaconfigSconfig = Def.setting("org.scalameta" %%% "metaconfig-sconfig" % metaconfigV) } diff --git a/project/plugins.sbt b/project/plugins.sbt index 929f223b48..80fc50b9f8 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,6 +4,8 @@ resolvers ++= Seq( Resolver.bintrayIvyRepo("jetbrains", "sbt-plugins"), ) +val crossProjectV = "1.3.2" + addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.6.1") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.6.1") @@ -12,6 +14,10 @@ addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0") addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.4") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") -addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") +addSbtPlugin( + "org.portable-scala" % "sbt-scala-native-crossproject" % crossProjectV, +) +addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % crossProjectV) addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4") addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") diff --git a/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliOptionsUtils.scala b/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliOptionsUtils.scala new file mode 100644 index 0000000000..ed5ca142fb --- /dev/null +++ b/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliOptionsUtils.scala @@ -0,0 +1,8 @@ +package org.scalafmt.cli + +import java.io.PrintWriter + +private[scalafmt] trait CliOptionsUtils { + def getConsoleWriter(): Option[PrintWriter] = Option(System.console()) + .map(_.writer) +} diff --git a/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliUtils.scala b/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliUtils.scala new file mode 100644 index 0000000000..ea384367b5 --- /dev/null +++ b/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/CliUtils.scala @@ -0,0 +1,35 @@ +package org.scalafmt.cli + +import org.scalafmt.sysops.AbsoluteFile + +import com.martiansoftware.nailgun.NGContext + +private[scalafmt] trait CliUtils { + protected val isScalaNative: Boolean = false + + def nailMain(nGContext: NGContext): Unit = { + val workingDirectory = AbsoluteFile.fromPathIfAbsolute( + nGContext.getWorkingDirectory, + ).getOrElse { + throw new IllegalStateException( + s"Expected absolute path, " + + s"obtained nGContext.getWorkingDirectory = ${nGContext.getWorkingDirectory}", + ) + } + val exit = Cli.mainWithOptions( + nGContext.getArgs, + CliOptions.default.copy(common = + CliOptions.default.common.copy( + cwd = Some(workingDirectory), + out = nGContext.out, + in = nGContext.in, + err = nGContext.err, + ), + ), + ) + nGContext.exit(exit.code) + } + + protected def getDynamicRunner(): ScalafmtRunner = ScalafmtDynamicRunner + +} diff --git a/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/TermUtils.scala b/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/TermUtils.scala new file mode 100644 index 0000000000..b2fc2f9297 --- /dev/null +++ b/scalafmt-cli/jvm/src/main/scala/org/scalafmt/cli/TermUtils.scala @@ -0,0 +1,13 @@ +package org.scalafmt.cli + +import java.sql.Timestamp + +private[scalafmt] trait TermUtils { + + // Copy/pasted over from coursier, but unused in scalafmt + private val format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + protected def formatTimestamp(ts: Long): String = format + .format(new Timestamp(ts)) + + def noConsole = System.console() == null +} diff --git a/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliOptionsUtils.scala b/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliOptionsUtils.scala new file mode 100644 index 0000000000..c3645a2c52 --- /dev/null +++ b/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliOptionsUtils.scala @@ -0,0 +1,7 @@ +package org.scalafmt.cli + +import java.io.PrintWriter + +private[scalafmt] trait CliOptionsUtils { + def getConsoleWriter(): Option[PrintWriter] = None +} diff --git a/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliUtils.scala b/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliUtils.scala new file mode 100644 index 0000000000..48e2cdc40f --- /dev/null +++ b/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/CliUtils.scala @@ -0,0 +1,7 @@ +package org.scalafmt.cli + +private[scalafmt] trait CliUtils { + protected val isScalaNative: Boolean = true + + protected def getDynamicRunner(): ScalafmtRunner = ??? +} diff --git a/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/TermUtils.scala b/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/TermUtils.scala new file mode 100644 index 0000000000..54038f2210 --- /dev/null +++ b/scalafmt-cli/native/src/main/scala/org/scalafmt/cli/TermUtils.scala @@ -0,0 +1,9 @@ +package org.scalafmt.cli + +private[scalafmt] trait TermUtils { + + // Copy/pasted over from coursier, but not used in scalafmt + protected def formatTimestamp(ts: Long): String = ??? + + def noConsole = false +} diff --git a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/Cli.scala b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/Cli.scala index aae5e0f24b..5e7ccb1829 100644 --- a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/Cli.scala +++ b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/Cli.scala @@ -1,7 +1,6 @@ package org.scalafmt.cli import org.scalafmt.Versions.{stable => stableVersion} -import org.scalafmt.sysops.AbsoluteFile import java.nio.file.Files import java.nio.file.Paths @@ -10,31 +9,7 @@ import scala.io.Source import scala.util.Using import scala.util.control.NoStackTrace -import com.martiansoftware.nailgun.NGContext - -object Cli { - def nailMain(nGContext: NGContext): Unit = { - val workingDirectory = AbsoluteFile.fromPathIfAbsolute( - nGContext.getWorkingDirectory, - ).getOrElse { - throw new IllegalStateException( - s"Expected absolute path, " + - s"obtained nGContext.getWorkingDirectory = ${nGContext.getWorkingDirectory}", - ) - } - val exit = mainWithOptions( - nGContext.getArgs, - CliOptions.default.copy(common = - CliOptions.default.common.copy( - cwd = Some(workingDirectory), - out = nGContext.out, - in = nGContext.in, - err = nGContext.err, - ), - ), - ) - nGContext.exit(exit.code) - } +object Cli extends CliUtils { private def throwIfError(exit: ExitCode): Unit = if (exit != ExitCode.Ok) throw new RuntimeException(exit.toString) with NoStackTrace @@ -86,8 +61,8 @@ object Cli { case Right(runner) => runWithRunner(options, runner) } - private val isNativeImage: Boolean = "true" == - System.getProperty("scalafmt.native-image", "false") + private val isNative: Boolean = isScalaNative || + ("true" == System.getProperty("scalafmt.native-image", "false")) private def getProposedConfigVersion(options: CliOptions): String = s"version = $stableVersion" @@ -125,7 +100,7 @@ object Cli { case Right(`stableVersion`) => options.common.debug.println(s"Using core runner [$stableVersion]") Right(ScalafmtCoreRunner) - case Right(v) if isNativeImage => + case Right(v) if isNative => Left( s"""|error: invalid Scalafmt version. | @@ -142,7 +117,7 @@ object Cli { ) case Right(v) => options.common.debug.println(s"Using dynamic runner [$v]") - Right(ScalafmtDynamicRunner) + Right(getDynamicRunner()) } } diff --git a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/CliOptions.scala b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/CliOptions.scala index 3fd4a0e119..30c0848a13 100644 --- a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/CliOptions.scala +++ b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/CliOptions.scala @@ -20,7 +20,7 @@ import scala.util.matching.Regex import metaconfig.Configured -object CliOptions { +object CliOptions extends CliOptionsUtils { val default = CliOptions() /** Tries to read configuration from @@ -40,12 +40,14 @@ object CliOptions { if (parsed.quiet) Output.NoopStream else { val usesOut = parsed.stdIn || parsed.writeMode.usesOut - val cons = if (usesOut) System.console() else null - if (cons ne null) new Output.FromWriter(cons.writer()) - else new Output.FromStream( - if (parsed.noStdErr || !usesOut) parsed.common.out - else parsed.common.err, - ) + val consWriterOpt = if (usesOut) getConsoleWriter() else None + consWriterOpt match { + case Some(writer) => new Output.FromWriter(writer) + case None => new Output.FromStream( + if (parsed.noStdErr || !usesOut) parsed.common.out + else parsed.common.err, + ) + } } val common = parsed.common.copy( out = guardPrintStream(parsed.quiet && !parsed.stdIn)(parsed.common.out), diff --git a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala index 607a821250..823630ab6e 100644 --- a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala +++ b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala @@ -41,7 +41,7 @@ object ScalafmtCoreRunner extends ScalafmtRunner { val termDisplay = newTermDisplay(options, inputMethods, termDisplayMessage) val exitCode = new AtomicReference(ExitCode.Ok) Breaks.breakable { - inputMethods.par.foreach { inputMethod => + inputMethods.compatPar.foreach { inputMethod => val code = handleFile(inputMethod, options, adjustedScalafmtConf) exitCode.getAndUpdate(ExitCode.merge(code, _)) if (options.check && !code.isOk) Breaks.break diff --git a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/ScalafmtDynamicRunner.scala b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/ScalafmtDynamicRunner.scala index f37e36ea1c..e1118203c2 100644 --- a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/ScalafmtDynamicRunner.scala +++ b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/ScalafmtDynamicRunner.scala @@ -39,7 +39,7 @@ object ScalafmtDynamicRunner extends ScalafmtRunner { val exitCode = new AtomicReference(ExitCode.Ok) breakable { - inputMethods.par.foreach { inputMethod => + inputMethods.compatPar.foreach { inputMethod => try { val code = handleFile(inputMethod, session, options) exitCode.getAndUpdate(ExitCode.merge(code, _)) diff --git a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/TermDisplay.scala b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/TermDisplay.scala index 1cdb6246e1..ad239035c5 100644 --- a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/TermDisplay.scala +++ b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/TermDisplay.scala @@ -8,7 +8,6 @@ package org.scalafmt.cli */ import java.io.File import java.io.Writer -import java.sql.Timestamp import java.util.concurrent._ import scala.annotation.tailrec @@ -62,7 +61,7 @@ object Terminal { } -object TermDisplay { +object TermDisplay extends TermUtils { def defaultFallbackMode: Boolean = { val env0 = sys.env.get("COURSIER_PROGRESS").map(_.toLowerCase).collect { @@ -71,7 +70,7 @@ object TermDisplay { } def compatibilityEnv = sys.env.contains("COURSIER_NO_TERM") - def nonInteractive = System.console() == null + def nonInteractive = noConsole def insideEmacs = sys.env.contains("INSIDE_EMACS") def ci = sys.env.contains("CI") @@ -108,10 +107,6 @@ object TermDisplay { } } - private val format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss") - private def formatTimestamp(ts: Long): String = format - .format(new Timestamp(ts)) - private case class CheckUpdateInfo( currentTimeOpt: Option[Long], remoteTimeOpt: Option[Long], @@ -159,7 +154,7 @@ object TermDisplay { private var width = 80 private var currentHeight = 0 - private val q = new LinkedBlockingDeque[Message] + private val q = new LinkedBlockingQueue[Message] def update(): Unit = if (q.size() == 0) q.put(Message.Update) diff --git a/scalafmt-config/js/src/main/scala/org/scalafmt/config/PlatformConfig.scala b/scalafmt-config/js/src/main/scala/org/scalafmt/config/PlatformConfig.scala index 544a805f54..ff1345d752 100644 --- a/scalafmt-config/js/src/main/scala/org/scalafmt/config/PlatformConfig.scala +++ b/scalafmt-config/js/src/main/scala/org/scalafmt/config/PlatformConfig.scala @@ -1,5 +1,12 @@ package org.scalafmt.config +import java.nio.file.Path + +import metaconfig.Input +import metaconfig.MetaconfigParser + object PlatformConfig { + val isScalaNative = false implicit val parser = metaconfig.hocon.hoconMetaconfigParser + def metaconfigInputFromFile(input: Path) = Input.File(input) } diff --git a/scalafmt-config/jvm/src/main/scala/org/scalafmt/config/PlatformConfig.scala b/scalafmt-config/jvm/src/main/scala/org/scalafmt/config/PlatformConfig.scala index f350693583..2d23590747 100644 --- a/scalafmt-config/jvm/src/main/scala/org/scalafmt/config/PlatformConfig.scala +++ b/scalafmt-config/jvm/src/main/scala/org/scalafmt/config/PlatformConfig.scala @@ -1,8 +1,13 @@ package org.scalafmt.config +import java.nio.file.Path + +import metaconfig.Input import metaconfig.MetaconfigParser object PlatformConfig { + val isScalaNative = false implicit val parser: MetaconfigParser = metaconfig.typesafeconfig.typesafeConfigMetaconfigParser + def metaconfigInputFromFile(input: Path) = Input.File(input) } diff --git a/scalafmt-config/native/src/main/scala/org/scalafmt/config/PlatformConfig.scala b/scalafmt-config/native/src/main/scala/org/scalafmt/config/PlatformConfig.scala new file mode 100644 index 0000000000..35ed597179 --- /dev/null +++ b/scalafmt-config/native/src/main/scala/org/scalafmt/config/PlatformConfig.scala @@ -0,0 +1,15 @@ +package org.scalafmt.config + +import java.nio.file.Files +import java.nio.file.Path + +import metaconfig.Input +import metaconfig.MetaconfigParser + +object PlatformConfig { + val isScalaNative = true + implicit val parser: MetaconfigParser = + metaconfig.sconfig.sConfigMetaconfigParser + def metaconfigInputFromFile(input: Path) = Input + .String(new String(Files.readAllBytes(input))) +} diff --git a/scalafmt-config/shared/src/main/scala/org/scalafmt/config/ConfParsed.scala b/scalafmt-config/shared/src/main/scala/org/scalafmt/config/ConfParsed.scala index a861f36d36..5f3118e068 100644 --- a/scalafmt-config/shared/src/main/scala/org/scalafmt/config/ConfParsed.scala +++ b/scalafmt-config/shared/src/main/scala/org/scalafmt/config/ConfParsed.scala @@ -58,6 +58,6 @@ object ConfParsed { fromInput(Input.String(input), path) def fromPath(input: Path, path: Option[String] = None): ConfParsed = - apply(Configured.fromExceptionThrowing(Input.File(input)), path) + apply(Configured.fromExceptionThrowing(metaconfigInputFromFile(input)), path) } diff --git a/scalafmt-core/native/src/main/scala/org/scalafmt/internal/RegexCompat.scala b/scalafmt-core/native/src/main/scala/org/scalafmt/internal/RegexCompat.scala new file mode 100644 index 0000000000..d68e2349da --- /dev/null +++ b/scalafmt-core/native/src/main/scala/org/scalafmt/internal/RegexCompat.scala @@ -0,0 +1,222 @@ +package org.scalafmt.internal + +import java.util.regex.Pattern + +import scala.util.matching.Regex + +/* Before text matching (?=re), after text matching (?<=re) + * and more are incompatible in Scala Native, so custom functions + * have to be used. + * Scala Native uses an implementation of: + * https://github.com/google/re2/wiki/Syntax + */ + +object RegexCompat { + + /* Replaces '\\h', which is incompatible in Scala Native. + * Does not check correctness of the input regex string. + */ + private def fixHorizontalSpaceInRegex(reg: String) = { + + @inline + val replacingInsideClass = + "\t \u00A0\u1680\u180E\u2000-\u200A\u202F\u205F\u3000" + + @inline + val replacingOutsideClass = s"[$replacingInsideClass]" + + val sb = new StringBuilder() + var isInClass = false + var isEscaped = false + + for (char <- reg) char match { + case '\\' if !isEscaped => isEscaped = true + case 'h' if isEscaped => + sb.append(if (isInClass) replacingInsideClass else replacingOutsideClass) + isEscaped = false + case '[' if !isEscaped => + sb.append('[') + isInClass = true + case ']' if !isEscaped => + sb.append(']') + isInClass = false + case other => + if (isEscaped) { + isEscaped = false + sb.append('\\') + } + sb.append(other) + } + sb.toString() + } + + @inline + val trailingSpace = Pattern + .compile(fixHorizontalSpaceInRegex("\\h+$"), Pattern.MULTILINE) + + // "slc" stands for single-line comment + @inline + val slcLine = Pattern + .compile(fixHorizontalSpaceInRegex("^/\\/\\/*\\h*(.*?)\\h*$")) + + @inline + val slcDelim = Pattern.compile(fixHorizontalSpaceInRegex("\\h+")) + + // "mlc" stands for multi-line comment + @inline + val mlcHeader = Pattern + .compile(fixHorizontalSpaceInRegex("^/\\*\\h*(?:\n\\h*[*]*\\h*)?")) + + @inline + val mlcLineDelim = Pattern + .compile(fixHorizontalSpaceInRegex("\\h*\n\\h*[*]*\\h*")) + + @inline + val mlcParagraphEnd = Pattern.compile("[.:!?=]$") + + @inline + val mlcParagraphBeg = Pattern.compile("^(?:[-*@=]|\\d+[.:])") + + @inline + val leadingAsteriskSpace = Pattern + .compile(fixHorizontalSpaceInRegex("\n\\h*[*][^*]"), Pattern.MULTILINE) + + @inline + val docstringLine = Pattern.compile( + fixHorizontalSpaceInRegex("^(?:\\h*\\*)?(\\h*)(.*?)\\h*$"), + Pattern.MULTILINE, + ) + + @inline + val emptyLines = fixHorizontalSpaceInRegex("\\h*(\n\\h*\\*?\\h*)*") + + @inline + val emptyDocstring = Pattern.compile(s"^/\\*\\*$emptyLines\\*/$$") + + @inline + val onelineDocstring = { + val oneline = fixHorizontalSpaceInRegex("[^*\n\\h](?:[^\n]*[^\n\\h])?") + Pattern.compile(s"^/\\*\\*$emptyLines($oneline)$emptyLines\\*/$$") + } + + @inline + val docstringLeadingSpace = Pattern.compile(fixHorizontalSpaceInRegex("^\\h+")) + + @inline + def compileStripMarginPattern(pipe: Char) = Pattern + .compile(fixHorizontalSpaceInRegex(s"\n+\\h*?\\$pipe"), Pattern.MULTILINE) + + @inline + def compileStripMarginPatternWithLineContent(pipe: Char) = Pattern.compile( + fixHorizontalSpaceInRegex(s"\n(\\h*\\$pipe)?([^\n]*)"), + Pattern.MULTILINE, + ) + + val stripMarginPatternWithLineContent = + compileStripMarginPatternWithLineContent('|') + + // see: https://ammonite.io/#Save/LoadSession + @inline + val ammonitePattern: Regex = "(?:\\s*\\n@)".r + + @inline + val stripMarginPattern = Pattern.compile( + fixHorizontalSpaceInRegex("\n(\\h*\\|)?([^\n]*)"), + Pattern.MULTILINE, + ) + + // startAfterPattern and endBeforePattern should be unique in basePattern + // basePattern = startAfterPattern + matched pattern + endBeforePattern + private def replaceAll( + basePattern: Pattern, + startAfterPattern: Pattern, + endBeforePattern: Pattern, + baseText: String, + replacingText: String, + ): String = { + val sb = new java.lang.StringBuilder() + val matcher = basePattern.matcher(baseText) + var currPosition = 0 + while (matcher.find()) { + val start = matcher.start() + val end = matcher.end() + + sb.append(baseText, currPosition, start) + + val subtext = baseText.substring(start, end) + val startAfterMatcher = startAfterPattern.matcher(subtext) + val endBeforeMatcher = endBeforePattern.matcher(subtext) + + startAfterMatcher.find() + endBeforeMatcher.find() + + sb.append(startAfterMatcher.group()) + sb.append(replacingText) + sb.append(endBeforeMatcher.group()) + + currPosition = end + } + + sb.append(baseText, currPosition, baseText.length()) + sb.toString() + } + + @inline + private val leadingPipeSpace = compileStripMarginPattern('|') + + @inline + private def getStripMarginPattern(pipe: Char) = + if (pipe == '|') leadingPipeSpace else compileStripMarginPattern(pipe) + + private val startAfterForReplaceAllStripMargin = Pattern.compile("\n+") + @inline + def replaceAllStripMargin( + text: String, + spaces: String, + pipe: Char, + ): String = { + val endBefore = Pattern.compile(s"\\$pipe") + replaceAll( + getStripMarginPattern(pipe), + startAfterForReplaceAllStripMargin, + endBefore, + text, + spaces, + ) + } + + private val startAfterForReplaceAllLeadingAsterisk = Pattern.compile("\n") + private val endBeforeForReplaceAllLeadingAsterisk = Pattern + .compile("([*][^*])") + @inline + def replaceAllLeadingAsterisk(trimmed: String, spaces: String): String = + replaceAll( + leadingAsteriskSpace, + startAfterForReplaceAllLeadingAsterisk, + endBeforeForReplaceAllLeadingAsterisk, + trimmed, + spaces, + ) + + // replaces baseText.split("(?={beforeText})") + @inline + def splitByBeforeTextMatching( + baseText: String, + beforeText: String, + ): Array[String] = { + val beforeTextPattern = Pattern.compile(beforeText) + val matcher = beforeTextPattern.matcher(baseText) + + val res = new scala.collection.mutable.ArrayBuffer[String]() + var currPosition = 0 + while (matcher.find()) { + val start = matcher.start() + if (start != 0) res.append(baseText.substring(currPosition, start)) + + currPosition = start + } + res.append(baseText.substring(currPosition, baseText.size)) + + res.toArray + } +} diff --git a/scalafmt-sysops/jvm/src/main/scala-2.12/org/scalafmt/CompatCollections.scala b/scalafmt-sysops/jvm/src/main/scala-2.12/org/scalafmt/CompatCollections.scala new file mode 100644 index 0000000000..02f93ae678 --- /dev/null +++ b/scalafmt-sysops/jvm/src/main/scala-2.12/org/scalafmt/CompatCollections.scala @@ -0,0 +1,10 @@ +package org.scalafmt + +private[scalafmt] object CompatCollections { + val JavaConverters = scala.collection.JavaConverters + object ParConverters { + implicit class XtensionIterable[T](val col: Iterable[T]) extends AnyVal { + def compatPar = col.par + } + } +} diff --git a/scalafmt-sysops/jvm/src/main/scala-2.13/org/scalafmt/CompatCollections.scala b/scalafmt-sysops/jvm/src/main/scala-2.13/org/scalafmt/CompatCollections.scala new file mode 100644 index 0000000000..120651ffc0 --- /dev/null +++ b/scalafmt-sysops/jvm/src/main/scala-2.13/org/scalafmt/CompatCollections.scala @@ -0,0 +1,12 @@ +package org.scalafmt + +import scala.collection.parallel.CollectionConverters._ + +private[scalafmt] object CompatCollections { + val JavaConverters = scala.jdk.CollectionConverters + object ParConverters { + implicit class XtensionIterable[T](val col: Iterable[T]) extends AnyVal { + def compatPar = col.par + } + } +} diff --git a/scalafmt-sysops/jvm/src/main/scala/org/scalafmt/sysops/PlatformCompat.scala b/scalafmt-sysops/jvm/src/main/scala/org/scalafmt/sysops/PlatformCompat.scala new file mode 100644 index 0000000000..eccb4964bf --- /dev/null +++ b/scalafmt-sysops/jvm/src/main/scala/org/scalafmt/sysops/PlatformCompat.scala @@ -0,0 +1,5 @@ +package org.scalafmt.sysops + +private[sysops] object PlatformCompat { + def isScalaNative = false +} diff --git a/scalafmt-sysops/native/src/main/scala-2.12/org/scalafmt/CompatCollections.scala b/scalafmt-sysops/native/src/main/scala-2.12/org/scalafmt/CompatCollections.scala new file mode 100644 index 0000000000..6d6de9aec1 --- /dev/null +++ b/scalafmt-sysops/native/src/main/scala-2.12/org/scalafmt/CompatCollections.scala @@ -0,0 +1,11 @@ +package org.scalafmt + +object CompatCollections { + val JavaConverters = scala.collection.JavaConverters + + object ParConverters { + implicit class XtensionIterable[T](val col: Iterable[T]) extends AnyVal { + def compatPar = col.par + } + } +} diff --git a/scalafmt-sysops/native/src/main/scala-2.13/org/scalafmt/CompatCollections.scala b/scalafmt-sysops/native/src/main/scala-2.13/org/scalafmt/CompatCollections.scala new file mode 100644 index 0000000000..e9fc3fdcd0 --- /dev/null +++ b/scalafmt-sysops/native/src/main/scala-2.13/org/scalafmt/CompatCollections.scala @@ -0,0 +1,14 @@ +package org.scalafmt + +object CompatCollections { + val JavaConverters = scala.jdk.CollectionConverters + + // Parallel collections are not released yet for Scala Native: + // https://github.com/scala/scala-parallel-collections/issues/262 + // Once that releases, this should be able to be removed + object ParConverters { + implicit class XtensionIterable[T](val col: Iterable[T]) extends AnyVal { + def compatPar = col + } + } +} diff --git a/scalafmt-sysops/native/src/main/scala/org/scalafmt/sysops/PlatformCompat.scala b/scalafmt-sysops/native/src/main/scala/org/scalafmt/sysops/PlatformCompat.scala new file mode 100644 index 0000000000..bc37cc61f8 --- /dev/null +++ b/scalafmt-sysops/native/src/main/scala/org/scalafmt/sysops/PlatformCompat.scala @@ -0,0 +1,5 @@ +package org.scalafmt.sysops + +private[sysops] object PlatformCompat { + def isScalaNative = true +} diff --git a/scalafmt-sysops/shared/src/main/scala-2.12/org/scalafmt/CompatCollections.scala b/scalafmt-sysops/shared/src/main/scala-2.12/org/scalafmt/CompatCollections.scala deleted file mode 100644 index 84094035db..0000000000 --- a/scalafmt-sysops/shared/src/main/scala-2.12/org/scalafmt/CompatCollections.scala +++ /dev/null @@ -1,6 +0,0 @@ -package org.scalafmt - -private[scalafmt] object CompatCollections { - val JavaConverters = scala.collection.JavaConverters - object ParConverters -} diff --git a/scalafmt-sysops/shared/src/main/scala-2.13/org/scalafmt/CompatCollections.scala b/scalafmt-sysops/shared/src/main/scala-2.13/org/scalafmt/CompatCollections.scala deleted file mode 100644 index 392588673e..0000000000 --- a/scalafmt-sysops/shared/src/main/scala-2.13/org/scalafmt/CompatCollections.scala +++ /dev/null @@ -1,6 +0,0 @@ -package org.scalafmt - -private[scalafmt] object CompatCollections { - val JavaConverters = scala.jdk.CollectionConverters - val ParConverters = scala.collection.parallel.CollectionConverters -} diff --git a/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/FileOps.scala b/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/FileOps.scala index c08cf05ad7..dee9693c12 100644 --- a/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/FileOps.scala +++ b/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/FileOps.scala @@ -1,6 +1,7 @@ package org.scalafmt.sysops import org.scalafmt.CompatCollections.JavaConverters._ +import org.scalafmt.sysops.PlatformCompat import java.net.URI import java.net.URL @@ -75,11 +76,12 @@ object FileOps { } /** Reads file from file system or from http url */ - def readFile(filename: String)(implicit codec: Codec): String = - Try(new URL(filename)) match { - case Success(url) => readFile(url) - case _ => readFile(getFile(filename)) - } + def readFile(filename: String)(implicit codec: Codec): String = { + val urlOpt = + if (PlatformCompat.isScalaNative) None + else Try(new URL(filename)).toOption + urlOpt.fold(readFile(getFile(filename)))(readFile) + } def readFile(url: URL)(implicit codec: Codec): String = { val isFile = Option(url.getProtocol).forall("file".equalsIgnoreCase) diff --git a/scalafmt-tests-community/src/test/scala/org/scalafmt/community/TestHelpers.scala b/scalafmt-tests-community/src/test/scala/org/scalafmt/community/TestHelpers.scala index c7baf44c12..e050fd5c1b 100644 --- a/scalafmt-tests-community/src/test/scala/org/scalafmt/community/TestHelpers.scala +++ b/scalafmt-tests-community/src/test/scala/org/scalafmt/community/TestHelpers.scala @@ -99,13 +99,13 @@ object TestHelpers { val (dirs, files) = try ds.iterator().asScala.toList.partition(Files.isDirectory(_)) finally ds.close() - val fileStats = files.par.flatMap { x => + val fileStats = files.compatPar.flatMap { x => val fileStr = x.toString if (fileStr.endsWith(".scala") && !excluded(fileStr)) Some(runFile(styleName, x, fileStr)) else None }.reduceLeftOption(TestStats.merge) - val dirStats = dirs.par.flatMap(checkFilesRecursive(styleName, _)) + val dirStats = dirs.compatPar.flatMap(checkFilesRecursive(styleName, _)) .reduceLeftOption(TestStats.merge) fileStats.fold(dirStats)(x => dirStats.map(TestStats.merge(_, x)).orElse(fileStats), diff --git a/scalafmt-tests/shared/src/test/scala/org/scalafmt/ScalafmtProps.scala b/scalafmt-tests/jvm/src/test/scala/org/scalafmt/ScalafmtProps.scala similarity index 99% rename from scalafmt-tests/shared/src/test/scala/org/scalafmt/ScalafmtProps.scala rename to scalafmt-tests/jvm/src/test/scala/org/scalafmt/ScalafmtProps.scala index 74b3e5d427..0515d8dfb3 100644 --- a/scalafmt-tests/shared/src/test/scala/org/scalafmt/ScalafmtProps.scala +++ b/scalafmt-tests/jvm/src/test/scala/org/scalafmt/ScalafmtProps.scala @@ -24,7 +24,7 @@ class ScalafmtProps extends FunSuite with FormatAssertions { // TODO(olafur) remove once testkit 1.7 is out Corpus.fastparse .copy(Corpus.fastparse.url.replace("olafurpg", "scalameta")), - ).take(count).toBuffer.par + ).take(count).toBuffer.compatPar SyntaxAnalysis.run[Observation[Bug]](corpus) { file => val code = file.read try Scalafmt.format(code, config) match { diff --git a/scalafmt-tests/jvm/src/test/scala/org/scalafmt/cli/CliOptionsJVMTest.scala b/scalafmt-tests/jvm/src/test/scala/org/scalafmt/cli/CliOptionsJVMTest.scala new file mode 100644 index 0000000000..3228725e52 --- /dev/null +++ b/scalafmt-tests/jvm/src/test/scala/org/scalafmt/cli/CliOptionsJVMTest.scala @@ -0,0 +1,25 @@ +package org.scalafmt.cli + +import FileTestOps._ +import munit.FunSuite + +class CliOptionsJVMTest extends FunSuite { + + private val baseCliOptionsWithOut = baseCliOptions + .copy(common = baseCliOptions.common.copy(out = System.out)) + + Seq("--stdin", "--stdout").foreach { arg => + test(s"don't write info when using $arg") { + val options = Cli.getConfig(Array(arg), baseCliOptionsWithOut).get + val cons = System.console() + if (cons ne null) options.common.info match { + case x: Output.FromWriter if x.obj eq cons.writer() => + case x => fail(s"info should be writing to console: $x") + } + else options.common.info match { + case x: Output.FromStream if x.obj eq Output.NoopStream.printStream => + case x => fail(s"info should be writing to NoopStream: $x") + } + } + } +} diff --git a/scalafmt-tests/jvm/src/test/scala/org/scalafmt/sysops/FileOpsJVMTest.scala b/scalafmt-tests/jvm/src/test/scala/org/scalafmt/sysops/FileOpsJVMTest.scala new file mode 100644 index 0000000000..f90e4e982e --- /dev/null +++ b/scalafmt-tests/jvm/src/test/scala/org/scalafmt/sysops/FileOpsJVMTest.scala @@ -0,0 +1,14 @@ +package org.scalafmt.sysops + +class FileOpsJVMTest extends munit.FunSuite { + test("readFile with URL") { + val url = getClass.getResource("/readme.md") + val contents = FileOps.readFile(url.toString) + val expectedFirstLine = "# scalafmt tests\n" + val firstLine = contents.substring(0, expectedFirstLine.length) + assertEquals(firstLine, expectedFirstLine) + + assertEquals(FileOps.readFile(url), contents) + assertEquals(FileOps.readAsURL(url), contents) + } +} diff --git a/scalafmt-tests/shared/src/test/scala/org/scalafmt/cli/CliOptionsTest.scala b/scalafmt-tests/shared/src/test/scala/org/scalafmt/cli/CliOptionsTest.scala index a1f0423671..a484ce074c 100644 --- a/scalafmt-tests/shared/src/test/scala/org/scalafmt/cli/CliOptionsTest.scala +++ b/scalafmt-tests/shared/src/test/scala/org/scalafmt/cli/CliOptionsTest.scala @@ -111,19 +111,4 @@ class CliOptionsTest extends FunSuite { assertEquals(options.common.info.printStream, System.out) } - Seq("--stdin", "--stdout").foreach { arg => - test(s"don't write info when using $arg") { - val options = Cli.getConfig(Array(arg), baseCliOptionsWithOut).get - val cons = System.console() - if (cons ne null) options.common.info match { - case x: Output.FromWriter if x.obj eq cons.writer() => - case x => fail(s"info should be writing to console: $x") - } - else options.common.info match { - case x: Output.FromStream if x.obj eq Output.NoopStream.printStream => - case x => fail(s"info should be writing to NoopStream: $x") - } - } - } - } diff --git a/scalafmt-tests/shared/src/test/scala/org/scalafmt/cli/CliTest.scala b/scalafmt-tests/shared/src/test/scala/org/scalafmt/cli/CliTest.scala index 7a773da67d..7f9d2040c1 100644 --- a/scalafmt-tests/shared/src/test/scala/org/scalafmt/cli/CliTest.scala +++ b/scalafmt-tests/shared/src/test/scala/org/scalafmt/cli/CliTest.scala @@ -3,6 +3,7 @@ package org.scalafmt.cli import org.scalafmt.Error.NoMatchingFiles import org.scalafmt.Versions.{stable => stableVersion} import org.scalafmt.cli.FileTestOps._ +import org.scalafmt.config.PlatformConfig import org.scalafmt.config.ProjectFiles import org.scalafmt.config.ScalafmtConfig import org.scalafmt.sysops.AbsoluteFile @@ -716,7 +717,7 @@ trait CliTestBehavior { } class CliTest extends AbstractCliTest with CliTestBehavior { - testCli("1.6.0-RC4") // test for runDynamic + if (!PlatformConfig.isScalaNative) testCli("1.6.0-RC4") // test for runDynamic, incompatible with Scala Native testCli(stableVersion) // test for runScalafmt test(s"path-error") { diff --git a/scalafmt-tests/shared/src/test/scala/org/scalafmt/sysops/FileOpsTest.scala b/scalafmt-tests/shared/src/test/scala/org/scalafmt/sysops/FileOpsTest.scala index 7b1bb7ef7f..08108c0a9d 100644 --- a/scalafmt-tests/shared/src/test/scala/org/scalafmt/sysops/FileOpsTest.scala +++ b/scalafmt-tests/shared/src/test/scala/org/scalafmt/sysops/FileOpsTest.scala @@ -45,17 +45,6 @@ class FileOpsTest extends munit.FunSuite { assertEquals(FileOps.listFiles(subsubfile), Seq(subsubfile)) } - test("readFile with URL") { - val url = getClass.getResource("/readme.md") - val contents = FileOps.readFile(url.toString) - val expectedFirstLine = "# scalafmt tests\n" - val firstLine = contents.substring(0, expectedFirstLine.length) - assertEquals(firstLine, expectedFirstLine) - - assertEquals(FileOps.readFile(url), contents) - assertEquals(FileOps.readAsURL(url), contents) - } - } object FileOpsTest {