From 07c654b5f8b6c6b32f14098d07cecd2c3536e4ba Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Thu, 10 Oct 2019 00:42:21 +0300 Subject: [PATCH 01/18] First steps to 2.13 --- build.sbt | 10 +++++----- project/Dependencies.scala | 6 +++--- project/build.properties | 2 +- .../scala/org/scalafmt/cli/ScalafmtCoreRunner.scala | 3 ++- .../src/main/scala/org/scalafmt/cli/TermDisplay.scala | 2 +- .../org/scalafmt/config/ScalafmtConfDecoders.scala | 2 +- .../scala/org/scalafmt/config/ScalafmtRunner.scala | 4 ++-- .../main/scala/org/scalafmt/internal/FormatOps.scala | 2 +- .../scala/org/scalafmt/internal/FormatWriter.scala | 2 +- .../src/main/scala/org/scalafmt/internal/Router.scala | 2 +- .../src/main/scala/org/scalafmt/rewrite/Patch.scala | 1 + .../src/main/scala/org/scalafmt/rewrite/Rewrite.scala | 2 +- .../src/main/scala/org/scalafmt/util/GitOps.scala | 2 +- 13 files changed, 21 insertions(+), 19 deletions(-) diff --git a/build.sbt b/build.sbt index dcb14d01e7..de131f3e68 100644 --- a/build.sbt +++ b/build.sbt @@ -3,6 +3,7 @@ import sbtcrossproject.CrossPlugin.autoImport.crossProject def scala211 = "2.11.12" def scala212 = "2.12.8" +def scala213 = "2.13.0" inThisBuild( List( @@ -19,8 +20,8 @@ inThisBuild( url("https://geirsson.com") ) ), - scalaVersion := scala212, - crossScalaVersions := List(scala212, scala211), + scalaVersion := scala213, + crossScalaVersions := List(scala213, scala212, scala211), resolvers += Resolver.sonatypeRepo("releases"), libraryDependencies ++= List( scalatest.value % Test, @@ -89,10 +90,8 @@ lazy val core = crossProject(JVMPlatform) .in(file("scalafmt-core")) .settings( moduleName := "scalafmt-core", - addCompilerPlugin( - "org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full - ), buildInfoSettings, + scalacOptions ++= scalacJvmOptions.value, libraryDependencies ++= Seq( metaconfig.value, scalameta.value, @@ -122,6 +121,7 @@ import sbtassembly.AssemblyPlugin.defaultUniversalScript val scalacJvmOptions = Def.setting { CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, 11)) => Seq("-target:jvm-1.8") + case Some((2, 13)) => Seq("-Ymacro-annotations") case _ => Seq.empty } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 06639b7645..c4d489f46d 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -5,10 +5,10 @@ import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._ // scalafmt: { maxColumn = 120, style = defaultWithAlign } object Dependencies { - val metaconfigV = "0.8.3" + val metaconfigV = "0.9.4" val scalametaV = "4.2.3" - val scalatestV = "3.2.0-SNAP10" - val scalacheckV = "1.13.5" + val scalatestV = "3.0.8" + val scalacheckV = "1.14.2" val coursier = "1.0.3" val scalapb = Def.setting { diff --git a/project/build.properties b/project/build.properties index 7c58a83abf..8522443ded 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.6 +sbt.version=1.3.2 diff --git a/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala b/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala index 8ceb0df54f..16940bd3a5 100644 --- a/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala +++ b/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala @@ -38,7 +38,8 @@ object ScalafmtCoreRunner extends ScalafmtRunner { val termDisplay = newTermDisplay(options, inputMethods, termDisplayMessage) val exitCode = new AtomicReference(ExitCode.Ok) - inputMethods.par.foreach { inputMethod => + // TODO add parallel collections + inputMethods.foreach { inputMethod => val code = handleFile(inputMethod, options, scalafmtConf) exitCode.getAndUpdate(new UnaryOperator[ExitCode] { override def apply(t: ExitCode): ExitCode = diff --git a/scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala b/scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala index 130c51a2d6..38e06ef6f6 100644 --- a/scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala +++ b/scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala @@ -39,7 +39,7 @@ object Terminal { None implicit class Ansi(val output: Writer) extends AnyVal { - private def control(n: Int, c: Char) = output.write(s"\033[" + n + c) + private def control(n: Int, c: Char) = output.write(s"\\033[" + n + c) /** * Move up `n` squares diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfDecoders.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfDecoders.scala index e3880cd5ae..069873bb31 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfDecoders.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfDecoders.scala @@ -18,7 +18,7 @@ trait ScalafmtConfDecoders { implicit lazy val eventReader: ConfDecoder[FormatEvent => Unit] = ConfDecoder.instance[FormatEvent => Unit] { case _ => - Configured.Ok((_: FormatEvent) => Unit) + Configured.Ok((_: FormatEvent) => ()) } implicit lazy val parseReader: ConfDecoder[MetaParser] = { ReaderUtil.oneOf[MetaParser](parseSource, parseStat, parseCase) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtRunner.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtRunner.scala index f3a99a75c4..4f8942793f 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtRunner.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtRunner.scala @@ -16,7 +16,7 @@ import scala.meta.parsers.Parse */ case class ScalafmtRunner( debug: Boolean = false, - eventCallback: FormatEvent => Unit = _ => Unit, + eventCallback: FormatEvent => Unit = _ => (), parser: Parse[_ <: Tree] = Parse.parseSource, optimizer: ScalafmtOptimizer = ScalafmtOptimizer.default, maxStateVisits: Int = 1000000, @@ -84,7 +84,7 @@ object ScalafmtRunner { */ val default = ScalafmtRunner( debug = false, - eventCallback = _ => Unit, + eventCallback = _ => (), parser = scala.meta.parsers.Parse.parseSource, optimizer = ScalafmtOptimizer.default, maxStateVisits = 1000000 diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala index 21154d2281..32adcb1cce 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala @@ -16,7 +16,7 @@ import scala.meta.Tree import scala.meta.Type import scala.meta.prettyprinters.Structure import scala.meta.tokens.Token -import scala.meta.tokens.Token._ +import scala.meta.tokens.Token.{Space => _, _} import org.scalafmt.Error.CaseMissingArrow import org.scalafmt.config.{ DanglingExclude, diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala index d57546627f..05dfa43bdf 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala @@ -13,7 +13,7 @@ import scala.meta.Tree import scala.meta.Type import scala.meta.prettyprinters.Syntax import scala.meta.tokens.Token -import scala.meta.tokens.Token._ +import scala.meta.tokens.Token.{Space => _, _} import java.util.regex.Pattern import org.scalafmt.internal.FormatWriter.FormatLocation diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala index f1a1da7902..de4a91c81e 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala @@ -27,7 +27,7 @@ import scala.meta.{ } // Too many to import individually. -import scala.meta.tokens.Token._ +import scala.meta.tokens.Token.{Space => _, _} object Constants { val ShouldBeNewline = 100000 diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/Patch.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/Patch.scala index 664e55243b..da51421aec 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/Patch.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/Patch.scala @@ -58,6 +58,7 @@ object Patch { (tokenPatches) .groupBy(t => t.tok.start -> t.tok.end) .mapValues(_.reduce(merge).newTok) + .toMap input.toIterator .map(x => patchMap.getOrElse(x.start -> x.end, x.syntax)) .mkString diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/Rewrite.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/Rewrite.scala index 91098d48e1..57d04c7042 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/Rewrite.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/rewrite/Rewrite.scala @@ -35,7 +35,7 @@ object Rewrite { ) private def nameMap[T](t: sourcecode.Text[T]*): Map[String, T] = { - t.map(x => x.source -> x.value)(scala.collection.breakOut) + t.view.map(x => x.source -> x.value).toMap } val name2rewrite: Map[String, Rewrite] = nameMap[Rewrite]( diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/GitOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/GitOps.scala index 9396ec9e8b..008f84e00a 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/util/GitOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/util/GitOps.scala @@ -22,7 +22,7 @@ class GitOpsImpl(private[util] val workingDirectory: AbsoluteFile) private[util] def exec(cmd: Seq[String]): Try[Seq[String]] = { val gitRes: Try[String] = Try { val lastError = new StringBuilder - val swallowStderr = ProcessLogger(_ => Unit, err => lastError.append(err)) + val swallowStderr = ProcessLogger(_ => (), err => lastError.append(err)) try { sys.process .Process(cmd, workingDirectory.jfile) From 09ef02898a49b32ea8c67ffe7ca45d916073fab7 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Thu, 10 Oct 2019 19:10:30 +0300 Subject: [PATCH 02/18] Macros fixed for 2.12 and 2.11 --- build.sbt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index de131f3e68..ba2505badf 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ import sbtcrossproject.CrossPlugin.autoImport.crossProject def scala211 = "2.11.12" def scala212 = "2.12.8" -def scala213 = "2.13.0" +def scala213 = "2.13.1" inThisBuild( List( @@ -98,7 +98,13 @@ lazy val core = crossProject(JVMPlatform) // scala-reflect is an undeclared dependency of fansi, see #1252. // Scalafmt itself does not require scala-reflect. "org.scala-lang" % "scala-reflect" % scalaVersion.value - ) + ), + libraryDependencies ++= { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 13)) => Seq.empty + case _ => Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)) + } + } ) // .jsSettings( // libraryDependencies ++= List( From bcc0c3bf61bc6182602a6da1398ee8d14ef2b77e Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Thu, 10 Oct 2019 19:21:52 +0300 Subject: [PATCH 03/18] Formatting --- build.sbt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index ba2505badf..8bcd39195f 100644 --- a/build.sbt +++ b/build.sbt @@ -100,9 +100,14 @@ lazy val core = crossProject(JVMPlatform) "org.scala-lang" % "scala-reflect" % scalaVersion.value ), libraryDependencies ++= { - CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, 13)) => Seq.empty - case _ => Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)) + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 13)) => Seq.empty + case _ => + Seq( + compilerPlugin( + "org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full + ) + ) } } ) From c6b6f55790e64ba2fcf0494c092f4be372500c5b Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Thu, 10 Oct 2019 19:29:25 +0300 Subject: [PATCH 04/18] Travis job for 2.13 --- .travis.yml | 6 ++++++ build.sbt | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 586f729553..1e13d7dd7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,12 +16,18 @@ jobs: - env: TEST="2.12" jdk: openjdk8 script: sbt ci-test + - env: TEST="2.13" + jdk: openjdk8 + script: sbt ci-test - env: TEST="2.11" jdk: openjdk11 script: sbt ci-test - env: TEST="2.12" jdk: openjdk11 script: sbt ci-test + - env: TEST="2.13" + jdk: openjdk11 + script: sbt ci-test - stage: release jdk: openjdk8 script: sbt ci-release docs/docusaurusPublishGhpages diff --git a/build.sbt b/build.sbt index 8bcd39195f..3e5f95dfe6 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,8 @@ skip in publish := true commands += Command.command("ci-test") { s => val scalaVersion = sys.env.get("TEST") match { case Some("2.11") => scala211 - case _ => scala212 + case Some("2.12") => scala212 + case _ => scala213 } val docsTest = if (scalaVersion == scala212) "docs/run" else "version" s"++$scalaVersion" :: From a980e56d802045933a0fc5635848274ee72fb108 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Fri, 11 Oct 2019 23:20:35 +0300 Subject: [PATCH 05/18] Parallel collections, scalatags and mdoc --- build.sbt | 14 +++++++++++++- project/Dependencies.scala | 2 +- project/plugins.sbt | 2 +- .../org/scalafmt/cli/CompatParCollections.scala | 15 +++++++++++++++ .../org/scalafmt/cli/ScalafmtCoreRunner.scala | 2 ++ 5 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 scalafmt-cli/src/main/scala/org/scalafmt/cli/CompatParCollections.scala diff --git a/build.sbt b/build.sbt index 3e5f95dfe6..1faec73739 100644 --- a/build.sbt +++ b/build.sbt @@ -153,6 +153,15 @@ lazy val cli = project // undeclared transitive dependency of coursier-small "org.scala-lang.modules" %% "scala-xml" % "1.2.0" ), + libraryDependencies ++= { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 13)) => + Seq( + "org.scala-lang.modules" %% "scala-parallel-collections" % "0.2.0" + ) + case _ => Seq.empty + } + }, scalacOptions ++= scalacJvmOptions.value ) .dependsOn(coreJVM, dynamic) @@ -163,7 +172,10 @@ lazy val tests = project skip in publish := true, libraryDependencies ++= Seq( // Test dependencies - "com.lihaoyi" %% "scalatags" % "0.6.8", + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 13)) => "com.lihaoyi" %% "scalatags" % "0.7.0" + case _ => "com.lihaoyi" %% "scalatags" % "0.6.8" + }, "org.typelevel" %% "paiges-core" % "0.2.4", scalametaTestkit ) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c4d489f46d..4e9f2b760f 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -6,7 +6,7 @@ import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._ object Dependencies { val metaconfigV = "0.9.4" - val scalametaV = "4.2.3" + val scalametaV = "4.2.4" val scalatestV = "3.0.8" val scalacheckV = "1.14.2" val coursier = "1.0.3" diff --git a/project/plugins.sbt b/project/plugins.sbt index acd306b9bb..9410ae07bd 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,7 +4,7 @@ resolvers ++= Seq( Resolver.bintrayIvyRepo("jetbrains", "sbt-plugins") ) -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "1.2.7") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "1.3.5") addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.6.0-RC4") addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.4.31") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0") diff --git a/scalafmt-cli/src/main/scala/org/scalafmt/cli/CompatParCollections.scala b/scalafmt-cli/src/main/scala/org/scalafmt/cli/CompatParCollections.scala new file mode 100644 index 0000000000..ca695b04f9 --- /dev/null +++ b/scalafmt-cli/src/main/scala/org/scalafmt/cli/CompatParCollections.scala @@ -0,0 +1,15 @@ +package org.scalafmt.cli + +private[cli] object CompatParCollections { + val Converters = { + import Compat._ + { + import scala.collection.parallel._ + CollectionConverters + } + } + + object Compat { + object CollectionConverters + } +} diff --git a/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala b/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala index 16940bd3a5..7c694e9da2 100644 --- a/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala +++ b/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala @@ -1,4 +1,5 @@ package org.scalafmt.cli + import java.util.concurrent.atomic.{AtomicInteger, AtomicReference} import java.util.function.UnaryOperator @@ -6,6 +7,7 @@ import metaconfig.Configured import org.scalafmt.Error.{MisformattedFile, NoMatchingFiles} import org.scalafmt.{Formatted, Scalafmt, Versions} import org.scalafmt.config.{FilterMatcher, ScalafmtConfig} +import org.scalafmt.cli.CompatParCollections.Converters._ import org.scalafmt.util.OsSpecific import scala.meta.internal.tokenizers.PlatformTokenizerCache From 2122fdd9d28db8cc64647413b65db434f34a0f98 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Fri, 11 Oct 2019 23:27:36 +0300 Subject: [PATCH 06/18] Scalameta downgrade --- project/Dependencies.scala | 2 +- .../src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 4e9f2b760f..c4d489f46d 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -6,7 +6,7 @@ import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._ object Dependencies { val metaconfigV = "0.9.4" - val scalametaV = "4.2.4" + val scalametaV = "4.2.3" val scalatestV = "3.0.8" val scalacheckV = "1.14.2" val coursier = "1.0.3" diff --git a/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala b/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala index 7c694e9da2..fac18e4b0f 100644 --- a/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala +++ b/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala @@ -40,7 +40,6 @@ object ScalafmtCoreRunner extends ScalafmtRunner { val termDisplay = newTermDisplay(options, inputMethods, termDisplayMessage) val exitCode = new AtomicReference(ExitCode.Ok) - // TODO add parallel collections inputMethods.foreach { inputMethod => val code = handleFile(inputMethod, options, scalafmtConf) exitCode.getAndUpdate(new UnaryOperator[ExitCode] { From 10050279da5d0a5eae6337c7a3f458e1a4ff1814 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Sat, 12 Oct 2019 15:49:03 +0300 Subject: [PATCH 07/18] Token => T --- .../org/scalafmt/internal/FormatOps.scala | 140 ++++---- .../org/scalafmt/internal/FormatWriter.scala | 84 +++-- .../scala/org/scalafmt/internal/Router.scala | 298 +++++++++--------- 3 files changed, 246 insertions(+), 276 deletions(-) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala index 32adcb1cce..549309486a 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala @@ -1,42 +1,18 @@ package org.scalafmt.internal +import org.scalafmt.Error.CaseMissingArrow +import org.scalafmt.config.{DanglingExclude, ScalafmtConfig} +import org.scalafmt.internal.ExpiresOn.{Left, Right} +import org.scalafmt.internal.Length.Num +import org.scalafmt.internal.Policy.NoPolicy +import org.scalafmt.util._ + import scala.annotation.tailrec import scala.collection.mutable -import scala.meta.Case -import scala.meta.Ctor -import scala.meta.Decl -import scala.meta.Defn -import scala.meta.Import -import scala.meta.Name -import scala.meta.Pat -import scala.meta.Pkg -import scala.meta.Template -import scala.meta.Term -import scala.meta.Tree -import scala.meta.Type +import scala.meta.{Case, Ctor, Decl, Defn, Import, Init, Name, Pat, Pkg, Template, Term, Tree, Type} import scala.meta.prettyprinters.Structure import scala.meta.tokens.Token -import scala.meta.tokens.Token.{Space => _, _} -import org.scalafmt.Error.CaseMissingArrow -import org.scalafmt.config.{ - DanglingExclude, - IndentOperator, - ScalafmtConfig, - VerticalMultiline -} -import org.scalafmt.internal.ExpiresOn.Left -import org.scalafmt.internal.ExpiresOn.Right -import org.scalafmt.internal.Length.Num -import org.scalafmt.internal.Policy.NoPolicy -import org.scalafmt.util.CtorModifier -import org.scalafmt.util.StyleMap -import org.scalafmt.util.TokenOps -import org.scalafmt.util.TreeOps -import org.scalafmt.util.Whitespace -import org.scalafmt.util.Modifier -import org.scalafmt.util.RightParenOrBracket -import org.scalameta.logger -import scala.meta.Init +import scala.meta.tokens.{Token => T} /** * Helper functions for generating splits/policies for a given tree. @@ -145,7 +121,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { rightOwner match { case t: Term.Select if startsOpenApply && !isChildOfImport => val chain = getSelectChain(t) - if (openApply.is[LeftBrace] && isShortCurlyChain(chain)) None + if (openApply.is[T.LeftBrace] && isShortCurlyChain(chain)) None else Some(tok -> chain) case _ => None } @@ -194,7 +170,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { curr: FormatToken, accum: Int = 0 ): (Int, FormatToken) = { - if (!curr.left.is[Comment]) accum -> curr + if (!curr.left.is[T.Comment]) accum -> curr else { val tok = prev(curr) if (tok == curr) accum -> curr @@ -209,7 +185,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { curr: FormatToken, accum: Int = 0 ): (Int, FormatToken) = { - if (!curr.right.is[Comment]) accum -> curr + if (!curr.right.is[T.Comment]) accum -> curr else { val tok = next(curr) if (tok == curr) accum -> curr @@ -221,8 +197,8 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { @tailrec final def rhsOptimalToken(start: FormatToken): Token = start.right match { - case Comma() | LeftParen() | RightParen() | RightBracket() | Semicolon() | - RightArrow() | Equals() + case T.Comma() | T.LeftParen() | T.RightParen() | T.RightBracket() | T.Semicolon() | + T.RightArrow() | T.Equals() if next(start) != start && !startsNewBlock(start.right) && newlinesBetween(start.between) == 0 => @@ -248,11 +224,11 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { def isTripleQuote(token: Token): Boolean = token.syntax.startsWith("\"\"\"") def isMarginizedString(token: Token): Boolean = token match { - case start @ Interpolation.Start() => + case start @ T.Interpolation.Start() => val end = matchingParentheses(hash(start)) val afterEnd = next(leftTok2tok(end)) afterEnd.left.syntax == "." && afterEnd.right.syntax == "stripMargin" - case string: Constant.String => + case string: T.Constant.String => string.syntax.startsWith("\"\"\"") && { val afterString = next(leftTok2tok(string)) afterString.left.syntax == "." && @@ -264,7 +240,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { @tailrec final def startsStatement(tok: FormatToken): Boolean = { statementStarts.contains(hash(tok.right)) || - (tok.right.is[Comment] && tok.between.exists(_.is[LF]) && + (tok.right.is[T.Comment] && tok.between.exists(_.is[T.LF]) && startsStatement(next(tok))) } @@ -273,7 +249,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { def getExcludeIf( end: Token, - cond: Token => Boolean = _.is[RightBrace] + cond: Token => Boolean = _.is[T.RightBrace] ): Set[Range] = { if (cond(end)) // allow newlines in final {} block Set(Range(matchingParentheses(hash(end)).start, end.end)) @@ -281,7 +257,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { } def skipUnindent(token: Token): Boolean = { - token.is[LeftParen] && { + token.is[T.LeftParen] && { val owner = owners(token) val isSuperfluous = isSuperfluousParenthesis(token, owner) isSuperfluous && (owner match { @@ -324,7 +300,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { case procedure: Defn.Def if procedure.decltpe.isDefined && procedure.decltpe.get.tokens.isEmpty => - procedure.body.tokens.find(_.is[LeftBrace]) + procedure.body.tokens.find(_.is[T.LeftBrace]) case Defn.Def(_, _, _, _, _, b @ Term.Block(_)) => b.tokens.headOption case _: Ctor.Primary => @@ -332,11 +308,11 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { // This is a terrible terrible hack. Please consider removing this. // The RightParen() LeftBrace() pair is presumably a ") {" combination // at a class definition - case FormatToken(RightParen(), b @ LeftBrace(), _) => Some(b) + case FormatToken(T.RightParen(), b @ T.LeftBrace(), _) => Some(b) case _ => Some(matchingParentheses(hash(open))) } case _ => - tree.tokens.find(t => t.is[Equals] && owners(t) == tree) + tree.tokens.find(t => t.is[T.Equals] && owners(t) == tree) } }.getOrElse(tree.tokens.last) @@ -347,9 +323,9 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { val expire = matchingParentheses(hash(open)) Policy( { - case d @ Decision(t @ FormatToken(left, comma @ Comma(), _), splits) + case d @ Decision(t @ FormatToken(left, comma @ T.Comma(), _), splits) if noTrailingCommas && - !next(t).right.is[Comment] && + !next(t).right.is[T.Comment] && owners(open) == owners(comma) => Decision(t, splits.map { case x if x.modification == NoSplit => @@ -359,18 +335,18 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { // Newline on every comma. case d @ Decision( - t @ FormatToken(comma @ Comma(), right, between), + t @ FormatToken(comma @ T.Comma(), right, between), splits ) if owners(open) == owners(comma) && // TODO(olafur) what the right { decides to be single line? - !right.is[LeftBrace] && + !right.is[T.LeftBrace] && // If comment is bound to comma, see unit/Comment. - (!right.is[Comment] || - between.exists(_.is[LF])) => + (!right.is[T.Comment] || + between.exists(_.is[T.LF])) => Decision(t, splits.filter { x => val isNewline = x.modification.isNewline - if (noTrailingCommas && !right.is[Comment]) !isNewline + if (noTrailingCommas && !right.is[T.Comment]) !isNewline else isNewline }) }, @@ -398,7 +374,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { { case Decision(tok, s) if tok.right.end < expire.end && - (penalizeLambdas || !tok.left.is[RightArrow]) && !ignore(tok) => + (penalizeLambdas || !tok.left.is[T.RightArrow]) && !ignore(tok) => Decision(tok, s.map { case split if split.modification.isNewline || @@ -437,7 +413,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { def getArrow(caseStat: Case): Token = caseStat.tokens - .find(t => t.is[RightArrow] && owners(t) == caseStat) + .find(t => t.is[T.RightArrow] && owners(t) == caseStat) .getOrElse(throw CaseMissingArrow(caseStat)) def templateCurly(owner: Tree): Token = { @@ -445,7 +421,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { } def templateCurly(template: Template): Option[Token] = { - template.tokens.find(x => x.is[LeftBrace] && owners(x) == template) + template.tokens.find(x => x.is[T.LeftBrace] && owners(x) == template) } def safeFilterNewlines( @@ -456,15 +432,15 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { else Seq(Split(Newline, 0)) } - final def getElseChain(term: Term.If): Vector[KwElse] = { - term.tokens.find(x => x.is[KwElse] && owners(x) == term) match { - case Some(els @ KwElse()) => + final def getElseChain(term: Term.If): Vector[T.KwElse] = { + term.tokens.find(x => x.is[T.KwElse] && owners(x) == term) match { + case Some(els @ T.KwElse()) => val rest = term.elsep match { case t: Term.If => getElseChain(t) - case _ => Vector.empty[KwElse] + case _ => Vector.empty[T.KwElse] } els +: rest - case _ => Vector.empty[KwElse] + case _ => Vector.empty[T.KwElse] } } @@ -482,11 +458,11 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { * * @param dot the dot owned by the select. */ - def getSelectsLastToken(dot: Dot): Token = { + def getSelectsLastToken(dot: T.Dot): Token = { var curr = next(leftTok2tok(dot)) while (isOpenApply(curr.right, includeCurly = true, includeNoParens = true) && !statementStarts.contains(hash(curr.right))) { - if (curr.right.is[Dot]) { + if (curr.right.is[T.Dot]) { curr = next(leftTok2tok(curr.right)) } else { curr = leftTok2tok(matchingParentheses(hash(curr.right))) @@ -503,10 +479,10 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { } def chainOptimalToken(chain: Vector[Term.Select]): Token = { - val lastDotIndex = chain.last.tokens.lastIndexWhere(_.is[Dot]) + val lastDotIndex = chain.last.tokens.lastIndexWhere(_.is[T.Dot]) val lastDot = if (lastDotIndex != -1) - chain.last.tokens(dialect)(lastDotIndex).asInstanceOf[Dot] + chain.last.tokens(dialect)(lastDotIndex).asInstanceOf[T.Dot] else throw new IllegalStateException(s"Missing . in select ${chain.last}") lastToken(owners(getSelectsLastToken(lastDot))) @@ -526,7 +502,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { val style = styleMap.at(formatToken) val modification = newlines2Modification( formatToken.between, - rightIsComment = formatToken.right.isInstanceOf[Comment] + rightIsComment = formatToken.right.isInstanceOf[T.Comment] ) val indent = { if (style.verticalAlignMultilineOperators) { @@ -596,7 +572,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { * * the expire token is the closing }, otherwise it's bar. */ - def selectExpire(dot: Dot): Token = { + def selectExpire(dot: T.Dot): Token = { val owner = ownersMap(hash(dot)) (for { parent <- owner.parent @@ -637,12 +613,12 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { var expire = tree.tokens.head tree.tokens.foreach { case t if !inside && ((t, ownersMap(hash(t))) match { - case (LeftParen(), _: Term.Apply | _: Init) => + case (T.LeftParen(), _: Term.Apply | _: Init) => // TODO(olafur) https://github.com/scalameta/scalameta/issues/345 val x = true x // Type compounds can be inside defn.defs - case (LeftBrace(), Type.Refine(_, _)) => true + case (T.LeftBrace(), Type.Refine(_, _)) => true case _ => false }) => inside = true @@ -690,14 +666,14 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { } def isSingleIdentifierAnnotation(tok: FormatToken): Boolean = { - val toMatch = if (tok.right.is[RightParen]) { + val toMatch = if (tok.right.is[T.RightParen]) { // Hack to allow any annotations with arguments like @foo(1) prev(prev(leftTok2tok(matchingParentheses(hash(tok.right))))) } else { tok } toMatch match { - case FormatToken(At(), _: Ident, _) => true + case FormatToken(T.At(), _: T.Ident, _) => true case _ => false } } @@ -712,7 +688,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { else { Policy( { - case Decision(t @ FormatToken(_, right @ KwWith(), _), splits) + case Decision(t @ FormatToken(_, right @ T.KwWith(), _), splits) if owners.contains(ownersMap(hash(right))) => Decision(t, splits.filter(_.modification.isNewline)) }, @@ -767,7 +743,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { val clearQueues = Set.newBuilder[TokenHash] val forces = Set.newBuilder[Tree] tree.tokens.foreach { - case left @ LeftParen() `:owner:` (app: Term.Apply) + case left @ T.LeftParen() `:owner:` (app: Term.Apply) if app.args.length >= runner.optimizer.forceConfigStyleMinArgCount && distance(left, matching(left)) > maxDistance => forces += app @@ -791,12 +767,12 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { val close = matching(open) val indentParam = Num(style.continuationIndent.defnSite) val indentSep = Num((indentParam.n - 2).max(0)) - val isBracket = open.is[LeftBracket] + val isBracket = open.is[T.LeftBracket] @tailrec def loop(token: Token): Option[Token] = { leftTok2tok(matching(token)) match { - case FormatToken(RightParenOrBracket(), l @ LeftParen(), _) => + case FormatToken(RightParenOrBracket(), l @ T.LeftParen(), _) => loop(l) case f @ FormatToken(left @ RightParenOrBracket(), right, _) => lazy val isCtorModifier = @@ -806,7 +782,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { case Modifier() if isCtorModifier => // This case only applies to classes next(f).right match { - case x @ (_: Token.LeftParen | _: Token.LeftBracket) => + case x @ (_: T.LeftParen | _: T.LeftBracket) => loop(x) case _ => Some(left) @@ -855,7 +831,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { // Try to find the first paren. If found, then we are dealing with // a class with type AND value params. Otherwise it is a class with // just type params. - findFirst(afterTypes, lastParen)(t => t.left.is[LeftParen]) + findFirst(afterTypes, lastParen)(t => t.left.is[T.LeftParen]) .fold(base)(t => base.merge(OneArgOneLineSplit(t.left))) } else base } @@ -890,11 +866,11 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { // - There's an implicit keyword and newlineBeforeImplicitKW is enabled // - newlineAfterOpenParen is enabled // - Mixed-params case with constructor modifier `] private (` - case Decision(t @ FormatToken(open2 @ LeftParen(), right, _), _) => + case Decision(t @ FormatToken(open2 @ T.LeftParen(), right, _), _) => val close2 = matchingParentheses(hash(open2)) val prevT = prev(t).left - val isImplicitArgList = right.is[KwImplicit] + val isImplicitArgList = right.is[T.KwImplicit] val newlineBeforeImplicitEnabled = style.verticalMultiline.newlineBeforeImplicitKW || @@ -922,7 +898,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { .withIndent(indentParam, close2, Right) ) ) - case Decision(t @ FormatToken(KwImplicit(), _, _), _) + case Decision(t @ FormatToken(T.KwImplicit(), _, _), _) if (style.verticalMultiline.newlineAfterImplicitKW || style.newlines.afterImplicitKWInVerticalMultiline) => val split = Split(Newline, 0) Decision(t, Seq(split)) @@ -933,7 +909,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { val policy = oneLinePerArg.merge(paramGroupSplitter, lastParen.end) val firstIndent = - if (r.is[RightParen]) indentSep // An empty param group + if (r.is[T.RightParen]) indentSep // An empty param group else indentParam val singleLineExpire = @@ -964,7 +940,7 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { // Returns leading comment, if there exists one, otherwise formatToken.right @tailrec final def leadingComment(formatToken: FormatToken): Token = { - if (formatToken.newlinesBetween <= 1 && formatToken.left.is[Comment]) + if (formatToken.newlinesBetween <= 1 && formatToken.left.is[T.Comment]) leadingComment(prev(formatToken)) else formatToken.right } diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala index 05dfa43bdf..0ea0d348ab 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala @@ -1,25 +1,15 @@ package org.scalafmt.internal -import scala.annotation.tailrec -import scala.collection.mutable -import scala.meta.Decl -import scala.meta.Defn -import scala.meta.Mod -import scala.meta.Import -import scala.meta.Importer -import scala.meta.Pkg -import scala.meta.Term -import scala.meta.Tree -import scala.meta.Type -import scala.meta.prettyprinters.Syntax -import scala.meta.tokens.Token -import scala.meta.tokens.Token.{Space => _, _} import java.util.regex.Pattern import org.scalafmt.internal.FormatWriter.FormatLocation import org.scalafmt.util.TreeOps +import scala.annotation.tailrec +import scala.meta.tokens.Token +import scala.meta.tokens.{Token => T} import scala.meta.transversers.Traverser +import scala.meta.{Importer, Mod, Term, Tree} /** * Produces formatted output from sequence of splits. @@ -34,12 +24,12 @@ class FormatWriter(formatOps: FormatOps) { reconstructPath(tokens, splits, debug = false) { case (state, formatToken, whitespace, tokenAligns) => formatToken.left match { - case c: Comment => + case c: T.Comment => sb.append(formatComment(c, state.indentation)) - case token @ Interpolation.Part(_) => + case token @ T.Interpolation.Part(_) => sb.append(formatMarginizedString(token, state.indentation)) - case Constant.String(_) => // Ignore, see below. - case c: Constant.Long => + case T.Constant.String(_) => // Ignore, see below. + case c: T.Constant.Long => val syntax = c.syntax // longs can be written as hex literals like 0xFF123L. Dont uppercase the X if (syntax.startsWith("0x")) { @@ -48,9 +38,9 @@ class FormatWriter(formatOps: FormatOps) { } else { sb.append(initStyle.literals.long.process(syntax)) } - case c: Constant.Float => + case c: T.Constant.Float => sb.append(initStyle.literals.float.process(c.syntax)) - case c: Constant.Double => + case c: T.Constant.Double => sb.append(initStyle.literals.double.process(c.syntax)) case token => val rewrittenToken = @@ -69,7 +59,7 @@ class FormatWriter(formatOps: FormatOps) { formatToken.right match { // state.column matches the end of formatToken.right - case literal: Constant.String => + case literal: T.Constant.String => val column = if (state.splits.last.modification.isNewline) state.indentation else lastState.column + whitespace.length @@ -88,7 +78,7 @@ class FormatWriter(formatOps: FormatOps) { val leadingAsteriskSpace = Pattern.compile("\n *\\*(?!\\*)", Pattern.MULTILINE) - private def formatComment(comment: Comment, indent: Int): String = { + private def formatComment(comment: T.Comment, indent: Int): String = { val alignedComment = if (comment.syntax.startsWith("/*") && formatOps.initStyle.reformatDocstrings) { @@ -108,14 +98,14 @@ class FormatWriter(formatOps: FormatOps) { val leadingPipeSpace = Pattern.compile("\n *\\|", Pattern.MULTILINE) private def formatMarginizedString(token: Token, indent: Int): String = { if (!initStyle.assumeStandardLibraryStripMargin) token.syntax - else if (token.is[Interpolation.Part] || + else if (token.is[T.Interpolation.Part] || isMarginizedString(token)) { val firstChar: Char = token match { - case Interpolation.Part(_) => + case T.Interpolation.Part(_) => (for { parent <- owners(token).parent firstInterpolationPart <- parent.tokens.find( - _.is[Interpolation.Part] + _.is[T.Interpolation.Part] ) char <- firstInterpolationPart.syntax.headOption } yield char).getOrElse(' ') @@ -180,7 +170,7 @@ class FormatWriter(formatOps: FormatOps) { // becomes a problem, we could also precompute these locations. lazy val nextNonComment = locations .drop(i + 1) - .dropWhile(_.formatToken.right.isInstanceOf[Comment]) + .dropWhile(_.formatToken.right.isInstanceOf[T.Comment]) .headOption val whitespace = split.modification match { @@ -191,7 +181,7 @@ class FormatWriter(formatOps: FormatOps) { else 0 " " + (" " * (tokenAligns.getOrElse(tok, 0) + previousAlign)) case nl: NewlineT - if nl.acceptNoSplit && !tok.left.isInstanceOf[Comment] && + if nl.acceptNoSplit && !tok.left.isInstanceOf[T.Comment] && state.indentation >= previous.state.column => "" case nl: NewlineT @@ -199,12 +189,14 @@ class FormatWriter(formatOps: FormatOps) { state.indentation >= previous.state.column => " " case _: NewlineT - if tok.right.isInstanceOf[Comment] && - nextNonComment.exists(_.formatToken.right.isInstanceOf[Dot]) => + if tok.right.isInstanceOf[T.Comment] && + nextNonComment.exists( + _.formatToken.right.isInstanceOf[T.Dot] + ) => // TODO this could slow for really long chains and could be indexed if necessary. val existsPreviousDotCall = locations .take(i - 1) - .exists(_.formatToken.right.isInstanceOf[Dot]) + .exists(_.formatToken.right.isInstanceOf[T.Dot]) // TODO should this 2 be hard-coded, set to some other existing configurable parameter, or configurable? val extraIndent = if (existsPreviousDotCall) 0 else 2 "\n" + " " * (state.indentation + extraIndent) @@ -276,7 +268,7 @@ class FormatWriter(formatOps: FormatOps) { private def isCandidate(location: FormatLocation): Boolean = { val token = location.formatToken.right val code = token match { - case c: Comment if isSingleLineComment(c) => "//" + case c: T.Comment if isSingleLineComment(c) => "//" case t => t.syntax } styleMap.at(location.formatToken).alignMap.get(code).exists { ownerRegexp => @@ -301,7 +293,7 @@ class FormatWriter(formatOps: FormatOps) { formatToken match { // Corner case when line ends with comment // TODO(olafur) should this be part of owners? - case FormatToken(x, c: Comment, _) if isSingleLineComment(c) => + case FormatToken(x, c: T.Comment, _) if isSingleLineComment(c) => owners(x) case FormatToken(_, r, _) => owners(r) match { @@ -427,7 +419,7 @@ class FormatWriter(formatOps: FormatOps) { private trait CloseParenOrBracket private object CloseParenOrBracket { def unapply(token: Token): Boolean = - token.is[RightParen] || token.is[RightBracket] + token.is[T.RightParen] || token.is[T.RightBracket] } private def handleTrailingCommasAndWhitespace( @@ -462,7 +454,7 @@ class FormatWriter(formatOps: FormatOps) { // in the TrailingCommas.never branch, nor does it // try to add them in the TrainingCommas.always branch. lazy val rightIsCloseDelim = right - .is[CloseParenOrBracket] || (right.is[RightBrace] && isImport) + .is[CloseParenOrBracket] || (right.is[T.RightBrace] && isImport) initStyle.trailingCommas match { // foo( @@ -472,10 +464,10 @@ class FormatWriter(formatOps: FormatOps) { // // Insert a comma after b case TrailingCommas.always - if !left.is[Comma] && - !left.is[Comment] && - !left.is[LeftParen] && // skip empty parentheses - !formatToken.right.is[Comment] && + if !left.is[T.Comma] && + !left.is[T.Comment] && + !left.is[T.LeftParen] && // skip empty parentheses + !formatToken.right.is[T.Comment] && rightIsCloseDelim && isNewline => sb.append(",") sb.append(whitespace) @@ -487,10 +479,10 @@ class FormatWriter(formatOps: FormatOps) { // // Insert a comma after b (before comment) case TrailingCommas.always - if left.is[Comment] && !prevFormatToken.left.is[Comma] && - !prevFormatToken.left.is[Comment] && + if left.is[T.Comment] && !prevFormatToken.left.is[T.Comma] && + !prevFormatToken.left.is[T.Comment] && !prevNonComment(formatToken).left - .is[LeftParen] && // skip empty parentheses + .is[T.LeftParen] && // skip empty parentheses rightIsCloseDelim && isNewline => val indexOfComment = sb.lastIndexOf(left.syntax) val index = sb.lastIndexOf(prevFormatToken.left.syntax, indexOfComment) @@ -511,8 +503,8 @@ class FormatWriter(formatOps: FormatOps) { // // Remove the comma after b case TrailingCommas.never - if left.is[Comma] && rightIsCloseDelim && - !formatToken.right.is[Comment] && isNewline => + if left.is[T.Comma] && rightIsCloseDelim && + !formatToken.right.is[T.Comment] && isNewline => sb.deleteCharAt(sb.length - 1) sb.append(whitespace) @@ -523,7 +515,7 @@ class FormatWriter(formatOps: FormatOps) { // // Remove the comma after b (before comment) case TrailingCommas.never - if left.is[Comment] && prevFormatToken.left.is[Comma] && + if left.is[T.Comment] && prevFormatToken.left.is[T.Comma] && rightIsCloseDelim && isNewline => val indexOfComment = sb.lastIndexOf(left.syntax) val indexOfComma = @@ -542,8 +534,8 @@ class FormatWriter(formatOps: FormatOps) { // // Remove the comma after b case _ - if left.is[Comma] && rightIsCloseDelim && - !next(formatToken).left.is[Comment] && !isNewline => + if left.is[T.Comma] && rightIsCloseDelim && + !next(formatToken).left.is[T.Comment] && !isNewline => sb.deleteCharAt(sb.length - 1) case _ => sb.append(whitespace) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala index de4a91c81e..97253ea4b1 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/Router.scala @@ -10,6 +10,7 @@ import org.scalafmt.util._ import scala.collection.mutable import scala.language.implicitConversions import scala.meta.tokens.{Token, Tokens} +import scala.meta.tokens.{Token => T} import scala.meta.{ Case, Defn, @@ -26,9 +27,6 @@ import scala.meta.{ Type } -// Too many to import individually. -import scala.meta.tokens.Token.{Space => _, _} - object Constants { val ShouldBeNewline = 100000 val ShouldBeSingleLine = 30 @@ -68,15 +66,15 @@ class Router(formatOps: FormatOps) { val newlines = newlinesBetween(formatToken.between) formatToken match { - case FormatToken(_: BOF, _, _) => + case FormatToken(_: T.BOF, _, _) => Seq( Split(NoSplit, 0) ) - case FormatToken(_, _: EOF, _) => + case FormatToken(_, _: T.EOF, _) => Seq( Split(Newline, 0) // End files with trailing newline ) - case FormatToken(start @ Interpolation.Start(), _, _) => + case FormatToken(start @ T.Interpolation.Start(), _, _) => val isStripMargin = isMarginizedString(start) val end = matchingParentheses(hash(start)) val policy = @@ -91,8 +89,8 @@ class Router(formatOps: FormatOps) { Split(NoSplit, 0, ignoreIf = isStripMargin).withPolicy(policy) ) case FormatToken( - Interpolation.Id(_) | Interpolation.Part(_) | Interpolation.Start() | - Interpolation.SpliceStart(), + T.Interpolation.Id(_) | T.Interpolation.Part(_) | + T.Interpolation.Start() | T.Interpolation.SpliceStart(), _, _ ) => @@ -101,25 +99,25 @@ class Router(formatOps: FormatOps) { ) case FormatToken( _, - Interpolation.Part(_) | Interpolation.End() | - Interpolation.SpliceEnd(), + T.Interpolation.Part(_) | T.Interpolation.End() | + T.Interpolation.SpliceEnd(), _ ) => Seq( Split(NoSplit, 0) ) - case FormatToken(LeftBrace(), RightBrace(), _) => + case FormatToken(T.LeftBrace(), T.RightBrace(), _) => Seq( Split(NoSplit, 0) ) // Import - case FormatToken(Dot(), open @ LeftBrace(), _) + case FormatToken(T.Dot(), open @ T.LeftBrace(), _) if parents(rightOwner).exists(_.is[Import]) => Seq( Split(NoSplit, 0) ) // Import left brace - case FormatToken(open @ LeftBrace(), _, _) + case FormatToken(open @ T.LeftBrace(), _, _) if parents(leftOwner).exists(_.is[Import]) => val close = matchingParentheses(hash(open)) val disallowSingleLineComments = style.importSelectors != ImportSelectors.singleLine @@ -149,12 +147,12 @@ class Router(formatOps: FormatOps) { .withIndent(2, close, Right) ) // Interpolated string left brace - case FormatToken(open @ LeftBrace(), _, _) + case FormatToken(open @ T.LeftBrace(), _, _) if leftOwner.is[SomeInterpolate] => Seq( Split(NoSplit, 0) ) - case FormatToken(_, close @ RightBrace(), _) + case FormatToken(_, close @ T.RightBrace(), _) if parents(rightOwner).exists(_.is[Import]) || rightOwner.is[SomeInterpolate] => val isInterpolate = rightOwner.is[Term.Interpolate] @@ -165,14 +163,14 @@ class Router(formatOps: FormatOps) { 0 ) ) - case FormatToken(Dot(), underscore @ Underscore(), _) + case FormatToken(T.Dot(), underscore @ T.Underscore(), _) if parents(rightOwner).exists(_.is[Import]) => Seq( Split(NoSplit, 0) ) // { ... } Blocks - case tok @ FormatToken(open @ LeftBrace(), right, between) => + case tok @ FormatToken(open @ T.LeftBrace(), right, between) => val close = matchingParentheses(hash(open)) val newlineBeforeClosingCurly = newlineBeforeClosingCurlyPolicy(close) val selfAnnotation: Option[Tokens] = leftOwner match { @@ -193,7 +191,7 @@ class Router(formatOps: FormatOps) { .get(hash(right)) .collect { case owner: Term.Function => - val arrow = lastLambda(owner).tokens.find(_.is[RightArrow]) + val arrow = lastLambda(owner).tokens.find(_.is[T.RightArrow]) val expire = arrow.getOrElse(owner.params.last.tokens.last) val singleLineUntilArrow = newlineBeforeClosingCurly.andThen(SingleLineBlock(expire).f) @@ -202,7 +200,7 @@ class Router(formatOps: FormatOps) { .getOrElse { selfAnnotation match { case Some(tokens) => - val arrow = leftOwner.tokens.find(_.is[RightArrow]) + val arrow = leftOwner.tokens.find(_.is[T.RightArrow]) val singleLineUntilArrow = newlineBeforeClosingCurly.andThen( SingleLineBlock(arrow.getOrElse(tokens.last)).f ) @@ -240,7 +238,7 @@ class Router(formatOps: FormatOps) { .withPolicy(newlineBeforeClosingCurly) .withIndent(2, close, Right) ) - case FormatToken(arrow @ RightArrow(), right, _) + case FormatToken(arrow @ T.RightArrow(), right, _) if statementStarts.contains(hash(right)) && leftOwner.isInstanceOf[Term.Function] => val endOfFunction = lastToken( @@ -259,14 +257,15 @@ class Router(formatOps: FormatOps) { Split(Space, 0, ignoreIf = !canBeSpace), Split(afterCurlyNewlines, 1).withIndent(2, endOfFunction, Left) ) - case FormatToken(RightArrow(), right, _) if leftOwner.is[Term.Function] => + case FormatToken(T.RightArrow(), right, _) + if leftOwner.is[Term.Function] => val (endOfFunction, expiresOn) = functionExpire( leftOwner.asInstanceOf[Term.Function] ) val hasBlock = - nextNonComment(formatToken).right.isInstanceOf[LeftBrace] + nextNonComment(formatToken).right.isInstanceOf[T.LeftBrace] val indent = // don't indent if the body is empty `{ x => }` - if (isEmptyFunctionBody(leftOwner) && !right.is[Comment]) 0 + if (isEmptyFunctionBody(leftOwner) && !right.is[T.Comment]) 0 else 2 Seq( Split(Space, 0, ignoreIf = isSingleLineComment(right)) @@ -276,10 +275,10 @@ class Router(formatOps: FormatOps) { .withIndent(indent, endOfFunction, expiresOn) ) // Case arrow - case tok @ FormatToken(arrow @ RightArrow(), right, between) + case tok @ FormatToken(arrow @ T.RightArrow(), right, between) if leftOwner.isInstanceOf[Case] => right match { - case LeftBrace() => + case T.LeftBrace() => // Redundant {} block around case statements. Seq( Split(Space, 0).withIndent( @@ -298,7 +297,7 @@ class Router(formatOps: FormatOps) { ) } // New statement - case tok @ FormatToken(Semicolon(), right, between) + case tok @ FormatToken(T.Semicolon(), right, between) if startsStatement(tok) && newlines == 0 => val expire = statementStarts(hash(right)).tokens.last Seq( @@ -314,10 +313,10 @@ class Router(formatOps: FormatOps) { shouldGet2xNewlines(tok, style, owners) ) val expire = rightOwner.tokens - .find(_.is[Equals]) + .find(_.is[T.Equals]) .map { equalsToken => val equalsFormatToken = leftTok2tok(equalsToken) - if (equalsFormatToken.right.is[LeftBrace]) { + if (equalsFormatToken.right.is[T.LeftBrace]) { equalsFormatToken.right } else { equalsToken @@ -326,13 +325,13 @@ class Router(formatOps: FormatOps) { .getOrElse(rightOwner.tokens.last) val isAnnotation = - right.is[At] || isSingleIdentifierAnnotation(prev(tok)) + right.is[T.At] || isSingleIdentifierAnnotation(prev(tok)) if (isAnnotation && style.optIn.annotationNewlines) Seq(Split(newlines2Modification(formatToken), 0)) else { val spaceCouldBeOk = newlines == 0 && - !left.is[Comment] && + !left.is[T.Comment] && right.is[Keyword] && isSingleIdentifierAnnotation(prev(tok)) Seq( @@ -348,21 +347,21 @@ class Router(formatOps: FormatOps) { ) } - case FormatToken(_, RightBrace(), _) => + case FormatToken(_, T.RightBrace(), _) => Seq( Split(xmlSpace(rightOwner), 0), Split(NewlineT(isDouble = newlines > 1), 0) ) - case FormatToken(left @ KwPackage(), _, _) if leftOwner.is[Pkg] => + case FormatToken(left @ T.KwPackage(), _, _) if leftOwner.is[Pkg] => Seq( Split(Space, 0) ) // Opening [ with no leading space. // Opening ( with no leading space. case FormatToken( - KwSuper() | KwThis() | Ident(_) | RightBracket() | RightBrace() | - RightParen() | Underscore(), - LeftParen() | LeftBracket(), + T.KwSuper() | T.KwThis() | T.Ident(_) | T.RightBracket() | + T.RightBrace() | T.RightParen() | T.Underscore(), + T.LeftParen() | T.LeftBracket(), _ ) if noSpaceBeforeOpeningParen(rightOwner) && { leftOwner.parent.forall { @@ -387,12 +386,12 @@ class Router(formatOps: FormatOps) { Split(modification, 0) ) // Defn.{Object, Class, Trait} - case tok @ FormatToken(KwObject() | KwClass() | KwTrait(), _, _) => + case tok @ FormatToken(T.KwObject() | T.KwClass() | T.KwTrait(), _, _) => val expire = defnTemplate(leftOwner) .flatMap(templateCurly) .getOrElse(leftOwner.tokens.last) val forceNewlineBeforeExtends = Policy({ - case Decision(t @ FormatToken(_, right @ KwExtends(), _), s) + case Decision(t @ FormatToken(_, right @ T.KwExtends(), _), s) if owners(right) == leftOwner => Decision(t, s.filter(_.modification.isNewline)) }, expire.end) @@ -403,17 +402,17 @@ class Router(formatOps: FormatOps) { Split(Space, 1).withPolicy(forceNewlineBeforeExtends) ) // DefDef - case tok @ FormatToken(KwDef(), name @ Ident(_), _) => + case tok @ FormatToken(T.KwDef(), name @ T.Ident(_), _) => Seq( Split(Space, 0) ) - case tok @ FormatToken(e @ Equals(), right, _) + case tok @ FormatToken(e @ T.Equals(), right, _) if defBody(leftOwner).isDefined => val expire = defBody(leftOwner).get.tokens.last val exclude = getExcludeIf( expire, { - case RightBrace() => true - case close @ RightParen() + case T.RightBrace() => true + case close @ T.RightParen() if opensConfigStyle( leftTok2tok(matchingParentheses(hash(close))) ) => @@ -422,7 +421,7 @@ class Router(formatOps: FormatOps) { // 1 // ) true - case RightParen() if !style.newlines.alwaysBeforeMultilineDef => + case T.RightParen() if !style.newlines.alwaysBeforeMultilineDef => true case _ => false } @@ -430,7 +429,7 @@ class Router(formatOps: FormatOps) { val rhsIsJsNative = isJsNative(right) right match { - case LeftBrace() => + case T.LeftBrace() => // The block will take care of indenting by 2. Seq(Split(Space, 0)) case _ => @@ -450,13 +449,13 @@ class Router(formatOps: FormatOps) { // Parameter opening for one parameter group. This format works // on the WHOLE defnSite (via policies) - case ft @ FormatToken((LeftParen() | LeftBracket()), _, _) + case ft @ FormatToken((T.LeftParen() | T.LeftBracket()), _, _) if (style.verticalMultiline.atDefnSite || style.verticalMultilineAtDefinitionSite) && isDefnSiteWithParams(leftOwner) => verticalMultiline(leftOwner, ft)(style) // Term.Apply and friends - case FormatToken(LeftParen() | LeftBracket(), right, between) + case FormatToken(T.LeftParen() | T.LeftBracket(), right, between) if style.optIn.configStyleArguments && (isDefnSite(leftOwner) || isCallSite(leftOwner)) && (opensConfigStyle(formatToken) || { @@ -488,10 +487,10 @@ class Router(formatOps: FormatOps) { .withIndent(extraIndent, right, Right) ) - case FormatToken(open @ (LeftParen() | LeftBracket()), right, between) + case FormatToken(open @ (T.LeftParen() | T.LeftBracket()), right, between) if style.binPack.unsafeDefnSite && isDefnSite(leftOwner) => val close = matchingParentheses(hash(open)) - val isBracket = open.is[LeftBracket] + val isBracket = open.is[T.LeftBracket] val indent = Num(style.continuationIndent.defnSite) if (isTuple(leftOwner)) { Seq( @@ -521,7 +520,7 @@ class Router(formatOps: FormatOps) { case _ => noSplitPenalizeNewlines } val noSplitModification = - if (right.is[Comment]) newlines2Modification(between) + if (right.is[T.Comment]) newlines2Modification(between) else NoSplit Seq( @@ -531,25 +530,25 @@ class Router(formatOps: FormatOps) { Split( Newline, (1 + nestingPenalty * nestingPenalty) * bracketMultiplier, - ignoreIf = right.is[RightParen] + ignoreIf = right.is[T.RightParen] ).withPolicy(penalizeBrackets(1)) .withIndent(indent, close, Left) ) } - case FormatToken(LeftParen() | LeftBracket(), _, _) + case FormatToken(T.LeftParen() | T.LeftBracket(), _, _) if style.binPack.unsafeCallSite && isCallSite(leftOwner) => val open = formatToken.left val close = matchingParentheses(hash(open)) val indent = getApplyIndent(leftOwner) val (lhs, args) = getApplyArgs(formatToken, leftOwner) - val optimal = leftOwner.tokens.find(_.is[Comma]).orElse(Some(close)) - val isBracket = open.is[LeftBracket] + val optimal = leftOwner.tokens.find(_.is[T.Comma]).orElse(Some(close)) + val isBracket = open.is[T.LeftBracket] // TODO(olafur) DRY. Same logic as in default. val exclude = if (isBracket) - insideBlock(formatToken, close, _.isInstanceOf[LeftBracket]) + insideBlock(formatToken, close, _.isInstanceOf[T.LeftBracket]) else - insideBlock(formatToken, close, x => x.isInstanceOf[LeftBrace]) + insideBlock(formatToken, close, x => x.isInstanceOf[T.LeftBrace]) val excludeRanges = exclude.map(parensRange) val unindent = UnindentAtExclude(exclude, Num(-style.continuationIndent.callSite)) @@ -571,15 +570,15 @@ class Router(formatOps: FormatOps) { .withPolicy(unindentPolicy) .withIndent(4, close, Left) ) - case FormatToken(LeftParen(), RightParen(), _) => + case FormatToken(T.LeftParen(), T.RightParen(), _) => Seq(Split(NoSplit, 0)) // If configured to skip the trailing space after `if` and other keywords, do so. - case FormatToken(KwIf() | KwFor() | KwWhile(), LeftParen(), _) + case FormatToken(T.KwIf() | T.KwFor() | T.KwWhile(), T.LeftParen(), _) if !style.spaces.afterKeywordBeforeParen => Seq(Split(NoSplit, 0)) - case tok @ FormatToken(LeftParen() | LeftBracket(), right, between) + case tok @ FormatToken(T.LeftParen() | T.LeftBracket(), right, between) if !isSuperfluousParenthesis(formatToken.left, leftOwner) && (!style.binPack.unsafeCallSite && isCallSite(leftOwner)) || (!style.binPack.unsafeDefnSite && isDefnSite(leftOwner)) => @@ -590,16 +589,16 @@ class Router(formatOps: FormatOps) { // parens furthest to the right. val lhsPenalty = treeDepth(lhs) - val isBracket = open.is[LeftBracket] + val isBracket = open.is[T.LeftBracket] val bracketMultiplier = if (isBracket) Constants.BracketPenalty else 1 val nestedPenalty = nestedApplies(leftOwner) val exclude = - if (isBracket) insideBlock(tok, close, _.is[LeftBracket]) + if (isBracket) insideBlock(tok, close, _.is[T.LeftBracket]) else - insideBlock(tok, close, x => x.is[LeftBrace]) + insideBlock(tok, close, x => x.is[T.LeftBrace]) val excludeRanges = exclude.map(parensRange) val indent = getApplyIndent(leftOwner) @@ -643,9 +642,9 @@ class Router(formatOps: FormatOps) { val oneArgOneLine = OneArgOneLineSplit(open) val newlineModification: Modification = - if (right.is[Comment] && newlinesBetween(between) == 0) + if (right.is[T.Comment] && newlinesBetween(between) == 0) Space - else if (right.is[LeftBrace]) NoSplit + else if (right.is[T.LeftBrace]) NoSplit else Newline val charactersInside = (close.start - open.end) - 2 @@ -692,9 +691,9 @@ class Router(formatOps: FormatOps) { } val noSplitModification: Modification = - if (formatToken.left.is[LeftParen] && + if (formatToken.left.is[T.LeftParen] && style.spaces.inParentheses) Space - else if (right.is[Comment]) newlines2Modification(between) + else if (right.is[T.Comment]) newlines2Modification(between) else NoSplit Seq( @@ -727,7 +726,7 @@ class Router(formatOps: FormatOps) { ) // Closing def site ): ReturnType - case FormatToken(left, Colon(), _) + case FormatToken(left, T.Colon(), _) if style.newlines.sometimesBeforeColonInMethodReturnType && defDefReturnType(leftOwner).isDefined => val expire = lastToken(defDefReturnType(rightOwner).get) @@ -742,7 +741,7 @@ class Router(formatOps: FormatOps) { .withIndent(style.continuationIndent.defnSite, expire, Left) .withPolicy(penalizeNewlines) ) - case FormatToken(Colon(), _, _) + case FormatToken(T.Colon(), _, _) if style.newlines.neverInResultType && defDefReturnType(leftOwner).isDefined => val expire = lastToken(defDefReturnType(leftOwner).get) @@ -752,23 +751,23 @@ class Router(formatOps: FormatOps) { ) ) - case FormatToken(LeftParen(), LeftBrace(), between) => + case FormatToken(T.LeftParen(), T.LeftBrace(), between) => Seq( Split(NoSplit, 0) ) - case FormatToken(_, LeftBrace(), _) if isXmlBrace(rightOwner) => + case FormatToken(_, T.LeftBrace(), _) if isXmlBrace(rightOwner) => Seq( Split(NoSplit, 0) ) - case FormatToken(RightBrace(), _, _) if isXmlBrace(leftOwner) => + case FormatToken(T.RightBrace(), _, _) if isXmlBrace(leftOwner) => Seq( Split(NoSplit, 0) ) // non-statement starting curly brace - case FormatToken(left, open @ LeftBrace(), _) => + case FormatToken(left, open @ T.LeftBrace(), _) => val close = matchingParentheses(hash(open)) - val isComma = left.is[Comma] + val isComma = left.is[T.Comma] val bodyHasNewlines = if (isComma) { open.pos.endLine != close.pos.startLine } else { @@ -785,12 +784,12 @@ class Router(formatOps: FormatOps) { ) // Delim - case FormatToken(_, Comma(), _) => + case FormatToken(_, T.Comma(), _) => Seq( Split(NoSplit, 0) ) // These are mostly filtered out/modified by policies. - case tok @ FormatToken(Comma(), right, _) => + case tok @ FormatToken(T.Comma(), right, _) => // TODO(olafur) DRY, see OneArgOneLine. val rhsIsAttachedComment = isSingleLineComment(tok.right) && newlinesBetween(tok.between) == 0 @@ -801,7 +800,7 @@ class Router(formatOps: FormatOps) { val nextComma: Option[FormatToken] = next( leftTok2tok(nextArg.tokens.last) ) match { - case t @ FormatToken(left @ Comma(), _, _) + case t @ FormatToken(left @ T.Comma(), _, _) if owners(left) == leftOwner => Some(t) case _ => None @@ -856,11 +855,11 @@ class Router(formatOps: FormatOps) { ) ) } - case FormatToken(_, Semicolon(), _) => + case FormatToken(_, T.Semicolon(), _) => Seq( Split(NoSplit, 0) ) - case FormatToken(KwReturn(), _, _) => + case FormatToken(T.KwReturn(), _, _) => val mod = leftOwner match { case Term.Return(unit @ Lit.Unit()) if unit.tokens.isEmpty => // Always force blank line for Unit "return". @@ -871,7 +870,7 @@ class Router(formatOps: FormatOps) { Seq( Split(mod, 0) ) - case FormatToken(left, Colon(), _) => + case FormatToken(left, T.Colon(), _) => val mod: Modification = rightOwner match { case tp: Type.Param => val contextOption = style.spaces.beforeContextBoundColon @@ -882,7 +881,7 @@ class Router(formatOps: FormatOps) { case _ => left match { - case ident: Ident => identModification(ident) + case ident: T.Ident => identModification(ident) case _ => NoSplit } } @@ -893,7 +892,7 @@ class Router(formatOps: FormatOps) { // an infix application or an if. For example, this is allowed: // val x = function(a, // b) - case FormatToken(tok @ Equals(), right, between) if (leftOwner match { + case FormatToken(tok @ T.Equals(), right, between) if (leftOwner match { case _: Defn.Type | _: Defn.Val | _: Defn.Var | _: Term.Assign | _: Term.Assign | _: Term.Assign => true @@ -927,7 +926,7 @@ class Router(formatOps: FormatOps) { else Newline val exclude = - insideBlock(formatToken, expire, _.isInstanceOf[LeftBrace]) + insideBlock(formatToken, expire, _.isInstanceOf[T.LeftBrace]) rhs match { case t: Term.ApplyInfix => Seq( @@ -962,14 +961,14 @@ class Router(formatOps: FormatOps) { .withIndent(2, expire, Left) ) } - case tok @ FormatToken(left, dot @ Dot() `:chain:` chain, _) - if !left.is[Underscore] => + case tok @ FormatToken(left, dot @ T.Dot() `:chain:` chain, _) + if !left.is[T.Underscore] => val nestedPenalty = nestedSelect(rightOwner) + nestedApplies(leftOwner) val lastToken = lastTokenInChain(chain) val optimalToken = chainOptimalToken(chain) val breakOnEveryDot = Policy( { - case Decision(t @ FormatToken(_, dot2 @ Dot(), _), s) + case Decision(t @ FormatToken(_, dot2 @ T.Dot(), _), s) if chain.contains(owners(dot2)) => val mod = if (style.optIn.breaksInsideChains && t.newlinesBetween == 0) @@ -1018,12 +1017,12 @@ class Router(formatOps: FormatOps) { .withIndent(2, optimalToken, Left) ) // ApplyUnary - case tok @ FormatToken(Ident(_), Literal(), _) + case tok @ FormatToken(T.Ident(_), Literal(), _) if leftOwner == rightOwner => Seq( Split(NoSplit, 0) ) - case FormatToken(op @ Ident(_), right, _) if leftOwner.parent.exists { + case FormatToken(op @ T.Ident(_), right, _) if leftOwner.parent.exists { case unary: Term.ApplyUnary => unary.op.tokens.head == op case _ => false @@ -1032,22 +1031,22 @@ class Router(formatOps: FormatOps) { Split(if (isSymbolicIdent(right)) Space else NoSplit, 0) ) // Annotations, see #183 for discussion on this. - case FormatToken(_, bind @ At(), _) if rightOwner.is[Pat.Bind] => + case FormatToken(_, bind @ T.At(), _) if rightOwner.is[Pat.Bind] => Seq( Split(Space, 0) ) - case FormatToken(bind @ At(), _, _) if leftOwner.is[Pat.Bind] => + case FormatToken(bind @ T.At(), _, _) if leftOwner.is[Pat.Bind] => Seq( Split(Space, 0) ) - case FormatToken(At(), right @ Delim(), _) => + case FormatToken(T.At(), right @ Delim(), _) => Seq(Split(NoSplit, 0)) - case FormatToken(At(), right @ Ident(_), _) => + case FormatToken(T.At(), right @ T.Ident(_), _) => // Add space if right starts with a symbol Seq(Split(identModification(right), 0)) // Template - case FormatToken(_, right @ KwExtends(), _) => + case FormatToken(_, right @ T.KwExtends(), _) => val template = defnTemplate(rightOwner) val lastToken = template .flatMap(templateCurly) @@ -1058,7 +1057,7 @@ class Router(formatOps: FormatOps) { lastToken, style.continuationIndent.extendSite ) - case FormatToken(_, KwWith(), _) => + case FormatToken(_, T.KwWith(), _) => rightOwner match { // something like new A with B with C case template: Template if template.parent.exists { p => @@ -1085,11 +1084,11 @@ class Router(formatOps: FormatOps) { { // Force template to be multiline. case d @ Decision( - FormatToken(open @ LeftBrace(), right, _), + FormatToken(open @ T.LeftBrace(), right, _), splits ) if !hasSelfAnnotation && - !right.is[RightBrace] && // corner case, body is {} + !right.is[T.RightBrace] && // corner case, body is {} childOf(template, owners(open)) => d.forceNewline }, @@ -1112,7 +1111,7 @@ class Router(formatOps: FormatOps) { Seq(Split(Space, 0)) } // If/For/While/For with ( - case FormatToken(open @ LeftParen(), _, _) if { + case FormatToken(open @ T.LeftParen(), _, _) if { val isSuperfluous = isSuperfluousParenthesis(open, leftOwner) leftOwner match { case _: Term.If | _: Term.While | _: Term.For | _: Term.ForYield @@ -1131,7 +1130,7 @@ class Router(formatOps: FormatOps) { .withIndent(indent, close, Left) .withPolicy(penalizeNewlines) ) - case FormatToken(KwIf(), _, _) if leftOwner.is[Term.If] => + case FormatToken(T.KwIf(), _, _) if leftOwner.is[Term.If] => val owner = leftOwner.asInstanceOf[Term.If] val expire = rhsOptimalToken( leftTok2tok( @@ -1141,7 +1140,8 @@ class Router(formatOps: FormatOps) { val elses = getElseChain(owner) val breakOnlyBeforeElse = Policy({ case d @ Decision(t, s) - if elses.contains(t.right) && !t.left.isInstanceOf[RightBrace] => + if elses.contains(t.right) && !t.left + .isInstanceOf[T.RightBrace] => d.onlyNewlines }, expire.end) Seq( @@ -1150,7 +1150,7 @@ class Router(formatOps: FormatOps) { .withPolicy(SingleLineBlock(expire)), Split(Space, 1).withPolicy(breakOnlyBeforeElse) ) - case FormatToken(close @ RightParen(), right, between) + case FormatToken(close @ T.RightParen(), right, between) if (leftOwner match { case _: Term.If | _: Term.For => true case _: Term.ForYield if style.indentYieldKeyword => true @@ -1170,32 +1170,32 @@ class Router(formatOps: FormatOps) { Space // Inline comment will force newline later. else Newline val exclude = - insideBlock(formatToken, expire, _.is[LeftBrace]).map(parensRange) + insideBlock(formatToken, expire, _.is[T.LeftBrace]).map(parensRange) Seq( Split(Space, 0, ignoreIf = attachedComment || newlines > 0) .withPolicy(SingleLineBlock(expire, exclude = exclude)), Split(newlineModification, 1).withIndent(2, expire, Left) ) - case FormatToken(RightBrace(), KwElse(), _) => + case FormatToken(T.RightBrace(), T.KwElse(), _) => if (style.newlines.alwaysBeforeElseAfterCurlyIf) Seq(Split(Newline, 0)) else if (leftOwner.is[Term.Block]) Seq(Split(Space, 0)) else Seq(Split(Newline, 1)) - case FormatToken(RightBrace(), KwYield(), _) => + case FormatToken(T.RightBrace(), T.KwYield(), _) => Seq( Split(Space, 0) ) - case FormatToken(_, KwElse() | KwYield(), _) => + case FormatToken(_, T.KwElse() | T.KwYield(), _) => val expire = rhsOptimalToken(leftTok2tok(rightOwner.tokens.last)) val exclude = - insideBlock(formatToken, expire, _.is[LeftBrace]).map(parensRange) + insideBlock(formatToken, expire, _.is[T.LeftBrace]).map(parensRange) Seq( Split(Space, 0, ignoreIf = newlines > 0) .withPolicy(SingleLineBlock(expire, exclude = exclude)), Split(Newline, 1) ) // Last else branch - case tok @ FormatToken(els @ KwElse(), _, _) - if !nextNonComment(tok).right.is[KwIf] => + case tok @ FormatToken(els @ T.KwElse(), _, _) + if !nextNonComment(tok).right.is[T.KwIf] => val expire = leftOwner match { case t: Term.If => t.elsep.tokens.last case x => throw new UnexpectedTree[Term.If](x) @@ -1211,19 +1211,19 @@ class Router(formatOps: FormatOps) { ) // Type variance - case tok @ FormatToken(Ident(_), Ident(_) | Underscore(), _) + case tok @ FormatToken(T.Ident(_), T.Ident(_) | T.Underscore(), _) if isTypeVariant(leftOwner) => Seq( Split(NoSplit, 0) ) // Var args - case FormatToken(_, Ident("*"), _) if rightOwner.is[Type.Repeated] => + case FormatToken(_, T.Ident("*"), _) if rightOwner.is[Type.Repeated] => Seq( Split(NoSplit, 0) ) - case FormatToken(open @ LeftParen(), right, _) => + case FormatToken(open @ T.LeftParen(), right, _) => val owner = owners(open) val isConfig = opensConfigStyle(formatToken) val isSuperfluous = isSuperfluousParenthesis(open, owner) @@ -1233,8 +1233,8 @@ class Router(formatOps: FormatOps) { Decision(t, Seq(Split(Newline, 0))) }, close.end) val indent: Length = right match { - case KwIf() => StateColumn - case KwFor() if !style.indentYieldKeyword => StateColumn + case T.KwIf() => StateColumn + case T.KwFor() if !style.indentYieldKeyword => StateColumn case _ => Num(0) } Seq( @@ -1246,12 +1246,12 @@ class Router(formatOps: FormatOps) { .withPolicy(penalizeAllNewlines(close, 1)) ) // Infix operator. - case tok @ FormatToken(op @ Ident(_), right, between) + case tok @ FormatToken(op @ T.Ident(_), right, between) if isApplyInfix(op, leftOwner) => // TODO(olafur) move extractor into pattern match. val InfixApplication(_, op, args) = leftOwner.parent.get Seq(infixSplit(leftOwner, op, args, formatToken)) - case FormatToken(left, op @ Ident(_), between) + case FormatToken(left, op @ T.Ident(_), between) if isApplyInfix(op, rightOwner) => val InfixApplication(_, op, args) = rightOwner.parent.get Seq(infixSplit(rightOwner, op, args, formatToken)) @@ -1260,15 +1260,15 @@ class Router(formatOps: FormatOps) { optionalNewlines(hash(opt.right)) => Seq(Split(newlines2Modification(opt), 0)) // Pat - case tok @ FormatToken(Ident("|"), _, _) + case tok @ FormatToken(T.Ident("|"), _, _) if leftOwner.is[Pat.Alternative] => Seq( Split(Space, 0), Split(Newline, 1) ) case FormatToken( - Ident(_) | Literal() | Interpolation.End() | Xml.End(), - Ident(_) | Literal() | Xml.Start(), + T.Ident(_) | Literal() | T.Interpolation.End() | T.Xml.End(), + T.Ident(_) | Literal() | T.Xml.Start(), _ ) => Seq( @@ -1276,25 +1276,25 @@ class Router(formatOps: FormatOps) { ) // Case - case FormatToken(_, KwMatch(), _) => + case FormatToken(_, T.KwMatch(), _) => Seq( Split(Space, 0) ) // Protected [] - case tok @ FormatToken(_, LeftBracket(), _) + case tok @ FormatToken(_, T.LeftBracket(), _) if isModPrivateProtected(leftOwner) => Seq( Split(NoSplit, 0) ) - case tok @ FormatToken(LeftBracket(), _, _) + case tok @ FormatToken(T.LeftBracket(), _, _) if isModPrivateProtected(leftOwner) => Seq( Split(NoSplit, 0) ) // Case - case tok @ FormatToken(cs @ KwCase(), _, _) if leftOwner.is[Case] => + case tok @ FormatToken(cs @ T.KwCase(), _, _) if leftOwner.is[Case] => val owner = leftOwner.asInstanceOf[Case] val arrow = getArrow(owner) // TODO(olafur) expire on token.end to avoid this bug. @@ -1315,7 +1315,7 @@ class Router(formatOps: FormatOps) { { case d @ Decision(t @ FormatToken(`arrow`, right, between), s) // TODO(olafur) any other corner cases? - if !right.isInstanceOf[LeftBrace] && + if !right.isInstanceOf[T.LeftBrace] && !isAttachedSingleLineComment(right, between) => Decision(t, s.filter(_.modification.isNewline)) }, @@ -1325,9 +1325,10 @@ class Router(formatOps: FormatOps) { .withIndent(2, expire, Left) // case body indented by 2. .withIndent(2, arrow, Left) // cond body indented by 4. ) - case tok @ FormatToken(_, cond @ KwIf(), _) if rightOwner.is[Case] => + case tok @ FormatToken(_, cond @ T.KwIf(), _) if rightOwner.is[Case] => val arrow = getArrow(rightOwner.asInstanceOf[Case]) - val exclude = insideBlock(tok, arrow, _.is[LeftBrace]).map(parensRange) + val exclude = + insideBlock(tok, arrow, _.is[T.LeftBrace]).map(parensRange) val singleLine = SingleLineBlock(arrow, exclude = exclude) Seq( @@ -1335,23 +1336,23 @@ class Router(formatOps: FormatOps) { Split(Newline, 1).withPolicy(penalizeNewlineByNesting(cond, arrow)) ) // Inline comment - case FormatToken(_, c: Comment, between) => + case FormatToken(_, c: T.Comment, between) => Seq(Split(newlines2Modification(between), 0)) // Commented out code should stay to the left - case FormatToken(c: Comment, _, between) if c.syntax.startsWith("//") => + case FormatToken(c: T.Comment, _, between) if c.syntax.startsWith("//") => Seq(Split(Newline, 0)) - case FormatToken(c: Comment, _, between) => + case FormatToken(c: T.Comment, _, between) => Seq(Split(newlines2Modification(between), 0)) // Term.ForYield - case tok @ FormatToken(_, arrow @ KwIf(), _) + case tok @ FormatToken(_, arrow @ T.KwIf(), _) if rightOwner.is[Enumerator.Guard] => Seq( // Either everything fits in one line or break on => Split(Space, 0, ignoreIf = newlines > 0), Split(Newline, 1) ) - case tok @ FormatToken(arrow @ LeftArrow(), _, _) + case tok @ FormatToken(arrow @ T.LeftArrow(), _, _) if leftOwner.is[Enumerator.Generator] => val lastToken = leftOwner.tokens.last val indent: Length = @@ -1361,7 +1362,7 @@ class Router(formatOps: FormatOps) { // Either everything fits in one line or break on => Split(Space, 0).withIndent(indent, lastToken, Left) ) - case FormatToken(KwYield(), _, _) if leftOwner.is[Term.ForYield] => + case FormatToken(T.KwYield(), _, _) if leftOwner.is[Term.ForYield] => if (style.newlines.avoidAfterYield && !rightOwner.is[Term.If]) { Seq(Split(Space, 0)) } else { @@ -1373,68 +1374,68 @@ class Router(formatOps: FormatOps) { ) } // Interpolation - case FormatToken(_, Interpolation.Id(_) | Xml.Start(), _) => + case FormatToken(_, T.Interpolation.Id(_) | T.Xml.Start(), _) => Seq( Split(Space, 0) ) - case FormatToken(Interpolation.Id(_) | Xml.Start(), _, _) => + case FormatToken(T.Interpolation.Id(_) | T.Xml.Start(), _, _) => Seq( Split(NoSplit, 0) ) // Throw exception - case FormatToken(KwThrow(), _, _) => + case FormatToken(T.KwThrow(), _, _) => Seq( Split(Space, 0) ) // Singleton types - case FormatToken(_, KwType(), _) if rightOwner.is[Type.Singleton] => + case FormatToken(_, T.KwType(), _) if rightOwner.is[Type.Singleton] => Seq( Split(NoSplit, 0) ) // seq to var args foo(seq:_*) - case FormatToken(Colon(), Underscore(), _) + case FormatToken(T.Colon(), T.Underscore(), _) if next(formatToken).right.syntax == "*" => Seq( Split(Space, 0) ) - case FormatToken(Underscore(), asterisk @ Ident("*"), _) - if prev(formatToken).left.is[Colon] => + case FormatToken(T.Underscore(), asterisk @ T.Ident("*"), _) + if prev(formatToken).left.is[T.Colon] => Seq( Split(NoSplit, 0) ) // Xml - case FormatToken(Xml.Part(_), _, _) => + case FormatToken(T.Xml.Part(_), _, _) => Seq( Split(NoSplit, 0) ) - case FormatToken(_, Xml.Part(_), _) => + case FormatToken(_, T.Xml.Part(_), _) => Seq( Split(NoSplit, 0) ) // Fallback - case FormatToken(_, Dot(), _) => + case FormatToken(_, T.Dot(), _) => Seq( Split(NoSplit, 0) ) - case FormatToken(left, Hash(), _) => + case FormatToken(left, T.Hash(), _) => Seq( Split(if (endsWithSymbolIdent(left)) Space else NoSplit, 0) ) - case FormatToken(Hash(), ident: Ident, _) => + case FormatToken(T.Hash(), ident: T.Ident, _) => val mod = if (TokenOps.isSymbolicIdent(ident)) Space else NoSplit Seq( Split(mod, 0) ) - case FormatToken(Dot(), Ident(_) | KwThis() | KwSuper(), _) => + case FormatToken(T.Dot(), T.Ident(_) | T.KwThis() | T.KwSuper(), _) => Seq( Split(NoSplit, 0) ) - case FormatToken(_, RightBracket(), _) => + case FormatToken(_, T.RightBracket(), _) => Seq( Split(NoSplit, 0) ) - case FormatToken(_, RightParen(), _) => + case FormatToken(_, T.RightParen(), _) => val mod = if (style.spaces.inParentheses && isDefnOrCallSite(rightOwner)) Space @@ -1443,7 +1444,7 @@ class Router(formatOps: FormatOps) { Split(mod, 0) ) case FormatToken(left, kw @ Keyword(), _) => - if (!left.is[RightBrace] && + if (!left.is[T.RightBrace] && Set("finally", "catch").contains(kw.syntax)) { Seq(Split(Newline, 0)) } else { @@ -1453,7 +1454,7 @@ class Router(formatOps: FormatOps) { Seq( Split(Space, 0) ) - case FormatToken(LeftBracket(), _, _) => + case FormatToken(T.LeftBracket(), _, _) => Seq( Split(NoSplit, 0) ) @@ -1461,11 +1462,11 @@ class Router(formatOps: FormatOps) { Seq( Split(Space, 0) ) - case FormatToken(Underscore(), Ident("*"), _) => + case FormatToken(T.Underscore(), T.Ident("*"), _) => Seq( Split(NoSplit, 0) ) - case FormatToken(RightArrow(), _, _) if leftOwner.is[Type.ByName] => + case FormatToken(T.RightArrow(), _, _) if leftOwner.is[Type.ByName] => val mod = if (!style.spaces.inByNameTypes) NoSplit else Space Seq( Split(mod, 0) @@ -1497,13 +1498,14 @@ class Router(formatOps: FormatOps) { formatToken match { // TODO(olafur) refactor into "global policy" // Only newlines after inline comments. - case FormatToken(c @ Comment(_), _, _) if c.syntax.startsWith("//") => + case FormatToken(c @ T.Comment(_), _, _) + if c.syntax.startsWith("//") => val newlineSplits = splits.filter { x => !x.ignoreIf && x.modification.isNewline } if (newlineSplits.isEmpty) Seq(Split(Newline, 0)) else newlineSplits - case FormatToken(_, c: Comment, between) + case FormatToken(_, c: T.Comment, between) if newlinesBetween(between) == 0 && c.syntax.startsWith("//") => splits.map( x => From 0fd77b044b4dcebe3b31c1616f3e17278c8bb0f7 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Sat, 12 Oct 2019 17:13:52 +0300 Subject: [PATCH 08/18] Tests compilation fix --- .../org/scalafmt/internal/FormatOps.scala | 35 ++++++++++++++----- .../test/scala/org/scalafmt/FormatTests.scala | 2 +- .../scala/org/scalafmt/util/HasTests.scala | 2 +- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala index 549309486a..8e49e01e7a 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala @@ -9,7 +9,21 @@ import org.scalafmt.util._ import scala.annotation.tailrec import scala.collection.mutable -import scala.meta.{Case, Ctor, Decl, Defn, Import, Init, Name, Pat, Pkg, Template, Term, Tree, Type} +import scala.meta.{ + Case, + Ctor, + Decl, + Defn, + Import, + Init, + Name, + Pat, + Pkg, + Template, + Term, + Tree, + Type +} import scala.meta.prettyprinters.Structure import scala.meta.tokens.Token import scala.meta.tokens.{Token => T} @@ -196,14 +210,17 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { nextNonCommentWithCount(curr)._2 @tailrec - final def rhsOptimalToken(start: FormatToken): Token = start.right match { - case T.Comma() | T.LeftParen() | T.RightParen() | T.RightBracket() | T.Semicolon() | - T.RightArrow() | T.Equals() - if next(start) != start && - !startsNewBlock(start.right) && - newlinesBetween(start.between) == 0 => - rhsOptimalToken(next(start)) - case _ => start.left + final def rhsOptimalToken(start: FormatToken): Token = { + import T._ + start.right match { + case Comma() | LeftParen() | RightParen() | RightBracket() | Semicolon() | + RightArrow() | Equals() + if next(start) != start && + !startsNewBlock(start.right) && + newlinesBetween(start.between) == 0 => + rhsOptimalToken(next(start)) + case _ => start.left + } } final def startsNewBlock(t: Token): Boolean = diff --git a/scalafmt-tests/src/test/scala/org/scalafmt/FormatTests.scala b/scalafmt-tests/src/test/scala/org/scalafmt/FormatTests.scala index 6bd99361e4..b95697d282 100644 --- a/scalafmt-tests/src/test/scala/org/scalafmt/FormatTests.scala +++ b/scalafmt-tests/src/test/scala/org/scalafmt/FormatTests.scala @@ -113,6 +113,6 @@ class FormatTests ) } yield () // Travis exits right after running tests. - if (sys.env.contains("TRAVIS")) Await.ready(k, 20 seconds) + if (sys.env.contains("TRAVIS")) Await.ready(k, 20.seconds) } } diff --git a/scalafmt-tests/src/test/scala/org/scalafmt/util/HasTests.scala b/scalafmt-tests/src/test/scala/org/scalafmt/util/HasTests.scala index d9691dd86e..38949bfbe3 100644 --- a/scalafmt-tests/src/test/scala/org/scalafmt/util/HasTests.scala +++ b/scalafmt-tests/src/test/scala/org/scalafmt/util/HasTests.scala @@ -198,7 +198,7 @@ trait HasTests extends FunSuiteLike with FormatAssertions { style: ScalafmtConfig, onlyOne: Boolean ): Array[FormatOutput] = { - val builder = mutable.ArrayBuilder.make[FormatOutput]() + val builder = mutable.ArrayBuilder.make[FormatOutput] new FormatWriter(Debug.formatOps) .reconstructPath(Debug.tokens, Debug.state.splits, debug = onlyOne) { case (_, token, whitespace, _) => From 27f61ae311569aebabd3fb4b599fb7acd5107b44 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Sat, 12 Oct 2019 17:14:12 +0300 Subject: [PATCH 09/18] .sbt cache disabled --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1e13d7dd7c..df341a65a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,6 @@ jobs: cache: directories: - - $HOME/.sbt - $HOME/.ivy2/cache - $HOME/.coursier - target/repos From 7b2dc9925e23b87b9dcfa1a7ab68f31a3163b30e Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Sun, 13 Oct 2019 16:24:26 +0300 Subject: [PATCH 10/18] Try to fix CI and small refactoring --- .travis.yml | 2 ++ .../src/main/scala/org/scalafmt/internal/FormatOps.scala | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index df341a65a7..c15a0c2ebb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,8 @@ jobs: cache: directories: + - $HOME/.sbt/1.0/dependency + - $HOME/.sbt/launchers - $HOME/.ivy2/cache - $HOME/.coursier - target/repos diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala index 8e49e01e7a..d846557134 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala @@ -211,10 +211,9 @@ class FormatOps(val tree: Tree, val initStyle: ScalafmtConfig) { @tailrec final def rhsOptimalToken(start: FormatToken): Token = { - import T._ start.right match { - case Comma() | LeftParen() | RightParen() | RightBracket() | Semicolon() | - RightArrow() | Equals() + case T.Comma() | T.LeftParen() | T.RightParen() | T.RightBracket() | + T.Semicolon() | T.RightArrow() | T.Equals() if next(start) != start && !startsNewBlock(start.right) && newlinesBetween(start.between) == 0 => From 736c436ce8927e40b9ecb31d9fbee65e35f38e0b Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Sun, 13 Oct 2019 16:36:37 +0300 Subject: [PATCH 11/18] Fixed par collections and ANSI literal --- .../src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala | 2 +- scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala b/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala index fac18e4b0f..6c85a3c5d2 100644 --- a/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala +++ b/scalafmt-cli/src/main/scala/org/scalafmt/cli/ScalafmtCoreRunner.scala @@ -40,7 +40,7 @@ object ScalafmtCoreRunner extends ScalafmtRunner { val termDisplay = newTermDisplay(options, inputMethods, termDisplayMessage) val exitCode = new AtomicReference(ExitCode.Ok) - inputMethods.foreach { inputMethod => + inputMethods.par.foreach { inputMethod => val code = handleFile(inputMethod, options, scalafmtConf) exitCode.getAndUpdate(new UnaryOperator[ExitCode] { override def apply(t: ExitCode): ExitCode = diff --git a/scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala b/scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala index 38e06ef6f6..d1d3975d91 100644 --- a/scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala +++ b/scalafmt-cli/src/main/scala/org/scalafmt/cli/TermDisplay.scala @@ -39,7 +39,7 @@ object Terminal { None implicit class Ansi(val output: Writer) extends AnyVal { - private def control(n: Int, c: Char) = output.write(s"\\033[" + n + c) + private def control(n: Int, c: Char) = output.write("\u001b[" + n + c) /** * Move up `n` squares From 62fb8db90ed93272edf68c319c2cc682824b2c06 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Mon, 14 Oct 2019 12:56:19 +0300 Subject: [PATCH 12/18] Benchmarks moved to 2.12 directory, sbt downgraded --- project/build.properties | 2 +- .../benchmarks/MacroBenchmark.scala | 39 +++---------------- .../scalafmt/benchmarks/FormatBenchmark.scala | 20 ++++++++++ .../scalafmt/benchmarks/MicroBenchmark.scala | 18 +-------- .../org/scalafmt/benchmarks/BenchmarkOK.scala | 1 + 5 files changed, 29 insertions(+), 51 deletions(-) rename scalafmt-benchmarks/src/main/{scala/org/scalafmt => scala-2.12}/benchmarks/MacroBenchmark.scala (67%) create mode 100644 scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/FormatBenchmark.scala rename scalafmt-benchmarks/src/test/{scala => scala-2.12}/org/scalafmt/benchmarks/BenchmarkOK.scala (96%) diff --git a/project/build.properties b/project/build.properties index 8522443ded..c0bab04941 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.2 +sbt.version=1.2.8 diff --git a/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/MacroBenchmark.scala b/scalafmt-benchmarks/src/main/scala-2.12/benchmarks/MacroBenchmark.scala similarity index 67% rename from scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/MacroBenchmark.scala rename to scalafmt-benchmarks/src/main/scala-2.12/benchmarks/MacroBenchmark.scala index faa9fdfda8..7fd8a8814c 100644 --- a/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/MacroBenchmark.scala +++ b/scalafmt-benchmarks/src/main/scala-2.12/benchmarks/MacroBenchmark.scala @@ -1,41 +1,14 @@ -package org.scalafmt.benchmarks - -import scala.collection.GenIterable -import scala.meta.Source -import scala.meta.testkit.Corpus -import scala.meta.testkit.CorpusFile -import scala.util.Try +package benchmarks import java.util.concurrent.TimeUnit -import org.openjdk.jmh.annotations.Benchmark -import org.openjdk.jmh.annotations.BenchmarkMode -import org.openjdk.jmh.annotations.Measurement -import org.openjdk.jmh.annotations.Mode -import org.openjdk.jmh.annotations.OutputTimeUnit -import org.openjdk.jmh.annotations.Scope -import org.openjdk.jmh.annotations.Setup -import org.openjdk.jmh.annotations.Warmup +import org.openjdk.jmh.annotations._ import org.scalafmt.Scalafmt -import org.scalafmt.config.RewriteSettings -import org.scalafmt.config.ScalafmtConfig -import org.scalafmt.rewrite.RedundantBraces -import org.scalafmt.rewrite.SortImports +import org.scalafmt.benchmarks.FormatBenchmark -trait FormatBenchmark { - def formatRewrite(code: String) = { - Scalafmt - .format( - code, - style = ScalafmtConfig.default.copy( - rewrite = RewriteSettings( - rules = Seq(SortImports, RedundantBraces) - ) - ) - ) - .get - } -} +import scala.collection.GenIterable +import scala.meta.testkit.Corpus +import scala.util.Try /** * Formats filename at with scalafmt. diff --git a/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/FormatBenchmark.scala b/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/FormatBenchmark.scala new file mode 100644 index 0000000000..8443377635 --- /dev/null +++ b/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/FormatBenchmark.scala @@ -0,0 +1,20 @@ +package org.scalafmt.benchmarks + +import org.scalafmt.Scalafmt +import org.scalafmt.config.{RewriteSettings, ScalafmtConfig} +import org.scalafmt.rewrite.{RedundantBraces, SortImports} + +trait FormatBenchmark { + def formatRewrite(code: String): String = { + Scalafmt + .format( + code, + style = ScalafmtConfig.default.copy( + rewrite = RewriteSettings( + rules = Seq(SortImports, RedundantBraces) + ) + ) + ) + .get + } +} diff --git a/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/MicroBenchmark.scala b/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/MicroBenchmark.scala index af3fc41ea9..e458f428e8 100644 --- a/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/MicroBenchmark.scala +++ b/scalafmt-benchmarks/src/main/scala/org/scalafmt/benchmarks/MicroBenchmark.scala @@ -3,25 +3,9 @@ package org.scalafmt.benchmarks import java.io.File import java.util.concurrent.TimeUnit -import org.openjdk.jmh.annotations.Benchmark -import org.openjdk.jmh.annotations.BenchmarkMode -import org.openjdk.jmh.annotations.Measurement -import org.openjdk.jmh.annotations.Mode -import org.openjdk.jmh.annotations.OutputTimeUnit -import org.openjdk.jmh.annotations.Scope -import org.openjdk.jmh.annotations.Setup -import org.openjdk.jmh.annotations.Warmup +import org.openjdk.jmh.annotations._ import org.scalafmt.Scalafmt import org.scalafmt.util.FileOps -import scala.meta.Source - -import org.scalafmt.config.RedundantBracesSettings -import org.scalafmt.config.RewriteSettings -import org.scalafmt.config.ScalafmtOptimizer -import org.scalafmt.config.ScalafmtRunner -import org.scalafmt.config.ScalafmtConfig -import org.scalafmt.rewrite.RedundantBraces -import org.scalafmt.rewrite.SortImports /** * Formats filename at [[path]] with scalafmt. diff --git a/scalafmt-benchmarks/src/test/scala/org/scalafmt/benchmarks/BenchmarkOK.scala b/scalafmt-benchmarks/src/test/scala-2.12/org/scalafmt/benchmarks/BenchmarkOK.scala similarity index 96% rename from scalafmt-benchmarks/src/test/scala/org/scalafmt/benchmarks/BenchmarkOK.scala rename to scalafmt-benchmarks/src/test/scala-2.12/org/scalafmt/benchmarks/BenchmarkOK.scala index f60ee5b12a..7780c4c43b 100644 --- a/scalafmt-benchmarks/src/test/scala/org/scalafmt/benchmarks/BenchmarkOK.scala +++ b/scalafmt-benchmarks/src/test/scala-2.12/org/scalafmt/benchmarks/BenchmarkOK.scala @@ -1,5 +1,6 @@ package org.scalafmt.benchmarks +import benchmarks.MacroBenchmark import org.scalafmt.benchmarks.Micro.ScalaJsFile import org.scalatest.FunSuite From d0873144bcb7e7a43613e6e9d345de619f74d3c7 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Tue, 15 Oct 2019 00:35:47 +0300 Subject: [PATCH 13/18] ScalafmtVersion introduced --- .../scalafmt/dynamic/ScalafmtVersion.scala | 36 +++++++++++++++++++ .../scala/tests/ScalafmtVersionSuite.scala | 33 +++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala create mode 100644 scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala diff --git a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala new file mode 100644 index 0000000000..2e4f35c9ba --- /dev/null +++ b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala @@ -0,0 +1,36 @@ +package org.scalafmt.dynamic + +import scala.util.control.{NonFatal, NoStackTrace} + +case class ScalafmtVersion(major: Int, minor: Int, patch: Int, rc: Int) { + private lazy val integerRepr: Int = + major * 1000 + minor * 100 + patch * 10 + rc + def <(other: ScalafmtVersion): Boolean = integerRepr < other.integerRepr + def >(other: ScalafmtVersion): Boolean = integerRepr > other.integerRepr + + override def toString: String = + s"$major.$minor.$patch" + (if (rc > 0) s"-RC$rc" else "") +} + +object ScalafmtVersion { + case class InvalidVersionException(version: String) + extends Exception(s"Invalid scalafmt version $version") + with NoStackTrace + + def parse(version: String): Either[InvalidVersionException, ScalafmtVersion] = + try { + val splitByDot = version.split("\\.") + val major = splitByDot(0).toInt + val minor = splitByDot(1).toInt + val splitByHyphen = splitByDot(2).split("-") + val patch = splitByHyphen(0).toInt + val rc = + if (splitByHyphen.size == 1) 0 + else if (splitByHyphen(1).startsWith("RC")) + splitByHyphen(1).drop(2).toInt + else throw InvalidVersionException(version) + Right(ScalafmtVersion(major, minor, patch, rc)) + } catch { + case e if NonFatal(e) => Left(InvalidVersionException(version)) + } +} diff --git a/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala b/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala new file mode 100644 index 0000000000..de463867f8 --- /dev/null +++ b/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala @@ -0,0 +1,33 @@ +package tests + +import org.scalatest.FunSuite +import org.scalafmt.dynamic.ScalafmtVersion +import org.scalafmt.dynamic.ScalafmtVersion.InvalidVersionException + +class ScalafmtVersionSuite extends FunSuite { + test("parse valid versions") { + assert(ScalafmtVersion.parse("2.0.0") == Right(ScalafmtVersion(2, 0, 0, 0))) + assert(ScalafmtVersion.parse("0.1.3") == Right(ScalafmtVersion(0, 1, 3, 0))) + assert(ScalafmtVersion.parse("2.0.0-RC4") == Right(ScalafmtVersion(2, 0, 0, 4))) + assert(ScalafmtVersion.parse("2.1.1") == Right(ScalafmtVersion(2, 1, 1, 0))) + } + + test("fail on invalid versions") { + assert(ScalafmtVersion.parse("2.0") == Left(InvalidVersionException("2.0"))) + assert(ScalafmtVersion.parse("v2.0.0") == Left(InvalidVersionException("v2.0.0"))) + assert(ScalafmtVersion.parse("avs") == Left(InvalidVersionException("avs"))) + assert(ScalafmtVersion.parse("1.2.3-M14") == Left(InvalidVersionException("1.2.3-M14"))) + } + + test("order versions") { + assert(ScalafmtVersion(2, 0, 0, 0) > ScalafmtVersion(1, 5, 1, 0)) + assert(ScalafmtVersion(0, 1, 2, 0) > ScalafmtVersion(0, 1, 1, 0)) + assert(ScalafmtVersion(0, 2, 2, 0) > ScalafmtVersion(0, 1, 2, 0)) + assert(ScalafmtVersion(2, 0, 0, 0) < ScalafmtVersion(2, 0, 0, 4)) + assert(ScalafmtVersion(0, 1, 2, 0) < ScalafmtVersion(1, 0, 0, 0)) + assert(ScalafmtVersion(0, 1, 8, 0) < ScalafmtVersion(0, 2, 2, 0)) + // check false positives + assert(!(ScalafmtVersion(2, 0, 0, 0) < ScalafmtVersion(1, 5, 1, 0))) + assert(!(ScalafmtVersion(0, 1, 8, 0) > ScalafmtVersion(0, 2, 2, 0))) + } +} From e3c6c977b9ed4365cfb006787553a875869eda94 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Tue, 15 Oct 2019 00:35:56 +0300 Subject: [PATCH 14/18] Fixed classpath errors for scala 2.12 artifacts --- build.sbt | 1 + .../scalafmt/dynamic/ScalafmtDynamic.scala | 2 + .../dynamic/ScalafmtDynamicDownloader.scala | 43 +++++++++++++------ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/build.sbt b/build.sbt index 1faec73739..f37a6c3bec 100644 --- a/build.sbt +++ b/build.sbt @@ -239,6 +239,7 @@ lazy val buildInfoSettings: Seq[Def.Setting[_]] = Seq( "stable" -> stableVersion.value, "scala" -> scalaVersion.value, "scala211" -> scala211, + "scala212" -> scala212, "coursier" -> coursier, "commit" -> sys.process.Process("git rev-parse HEAD").lineStream_!.head, "timestamp" -> System.currentTimeMillis().toString, diff --git a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala index 4ace0f5b5a..ed2a453226 100644 --- a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala +++ b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala @@ -169,6 +169,8 @@ final case class ScalafmtDynamic( ScalafmtDynamicError.CannotDownload(configPath, v, None) case DownloadUnknownError(v, cause) => ScalafmtDynamicError.CannotDownload(configPath, v, Option(cause)) + case InvalidVersionError(v, cause) => + ScalafmtDynamicError.CannotDownload(configPath, v, None) } .flatMap(resolveClassPath(configPath, _)) } diff --git a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala index 24b17d062b..64df043f6d 100644 --- a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala +++ b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala @@ -7,6 +7,8 @@ import java.nio.file.Path import coursierapi._ import scala.collection.JavaConverters._ import org.scalafmt.dynamic.ScalafmtDynamicDownloader._ +import org.scalafmt.dynamic.ScalafmtVersion +import org.scalafmt.dynamic.ScalafmtVersion.InvalidVersionException import scala.concurrent.duration.Duration import scala.util.Try @@ -19,7 +21,16 @@ class ScalafmtDynamicDownloader( ttl: Option[Duration] = None ) { - def download(version: String): Either[DownloadFailure, DownloadSuccess] = { + def download(version: String): Either[DownloadFailure, DownloadSuccess] = + ScalafmtVersion + .parse(version) + .left + .map(InvalidVersionError(version, _)) + .flatMap(download) + + def download( + version: ScalafmtVersion + ): Either[DownloadFailure, DownloadSuccess] = { Try { val settings = Fetch .create() @@ -35,20 +46,20 @@ class ScalafmtDynamicDownloader( .withCache(Cache.create()) val urls: Array[URL] = settings.fetch().asScala.iterator.map(_.toURI.toURL).toArray - DownloadSuccess(version, urls) + DownloadSuccess(version.toString, urls) }.toEither.left.map { case e: error.ResolutionError => - DownloadResolutionError(version, e) + DownloadResolutionError(version.toString, e) case e => - DownloadUnknownError(version, e) + DownloadUnknownError(version.toString, e) } } - private def dependencies(version: String): List[Dependency] = List( + private def dependencies(version: ScalafmtVersion): List[Dependency] = List( Dependency.of( organization(version), s"scalafmt-cli_${scalaBinaryVersion(version)}", - version + version.toString ), Dependency.of( "org.scala-lang", @@ -58,18 +69,20 @@ class ScalafmtDynamicDownloader( ) @inline - private def scalaBinaryVersion(version: String): String = - if (version.startsWith("0.")) "2.11" - else "2.12" + private def scalaBinaryVersion(version: ScalafmtVersion): String = + if (version < ScalafmtVersion(1, 0, 0, 0)) "2.11" + else if (version < ScalafmtVersion(2, 2, 0, 0)) "2.12" + else "2.13" @inline - private def scalaVersion(version: String): String = - if (version.startsWith("0.")) BuildInfo.scala211 + private def scalaVersion(version: ScalafmtVersion): String = + if (version < ScalafmtVersion(1, 0, 0, 0)) BuildInfo.scala211 + else if (version < ScalafmtVersion(2, 2, 0, 0)) BuildInfo.scala212 else BuildInfo.scala @inline - private def organization(version: String): String = - if (version.startsWith("1") || version.startsWith("0") || version == "2.0.0-RC1") { + private def organization(version: ScalafmtVersion): String = + if (version < ScalafmtVersion(2, 0, 0, 2)) { "com.geirsson" } else { "org.scalameta" @@ -100,4 +113,8 @@ object ScalafmtDynamicDownloader { ) extends DownloadFailure case class DownloadUnknownError(version: String, cause: Throwable) extends DownloadFailure + case class InvalidVersionError( + version: String, + cause: InvalidVersionException + ) extends DownloadFailure } From 82526fd9b9d9bb0b7552f62dcef4f3ed80bb10ab Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Tue, 15 Oct 2019 00:57:39 +0300 Subject: [PATCH 15/18] Fixed RC handling in ScalafmtVersion --- .../scalafmt/dynamic/ScalafmtVersion.scala | 11 ++++++++--- .../scala/tests/ScalafmtVersionSuite.scala | 19 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala index 2e4f35c9ba..42b364c0f9 100644 --- a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala +++ b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala @@ -4,9 +4,14 @@ import scala.util.control.{NonFatal, NoStackTrace} case class ScalafmtVersion(major: Int, minor: Int, patch: Int, rc: Int) { private lazy val integerRepr: Int = - major * 1000 + minor * 100 + patch * 10 + rc - def <(other: ScalafmtVersion): Boolean = integerRepr < other.integerRepr - def >(other: ScalafmtVersion): Boolean = integerRepr > other.integerRepr + major * 100 + minor * 10 + patch + + def <(other: ScalafmtVersion): Boolean = + if (integerRepr == other.integerRepr) + rc != 0 && (other.rc == 0 || rc < other.rc) + else integerRepr < other.integerRepr + + def >(other: ScalafmtVersion): Boolean = this != other && !(this < other) override def toString: String = s"$major.$minor.$patch" + (if (rc > 0) s"-RC$rc" else "") diff --git a/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala b/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala index de463867f8..163e3db516 100644 --- a/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala +++ b/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala @@ -8,25 +8,34 @@ class ScalafmtVersionSuite extends FunSuite { test("parse valid versions") { assert(ScalafmtVersion.parse("2.0.0") == Right(ScalafmtVersion(2, 0, 0, 0))) assert(ScalafmtVersion.parse("0.1.3") == Right(ScalafmtVersion(0, 1, 3, 0))) - assert(ScalafmtVersion.parse("2.0.0-RC4") == Right(ScalafmtVersion(2, 0, 0, 4))) + assert( + ScalafmtVersion.parse("2.0.0-RC4") == Right(ScalafmtVersion(2, 0, 0, 4)) + ) assert(ScalafmtVersion.parse("2.1.1") == Right(ScalafmtVersion(2, 1, 1, 0))) } test("fail on invalid versions") { assert(ScalafmtVersion.parse("2.0") == Left(InvalidVersionException("2.0"))) - assert(ScalafmtVersion.parse("v2.0.0") == Left(InvalidVersionException("v2.0.0"))) + assert( + ScalafmtVersion.parse("v2.0.0") == Left(InvalidVersionException("v2.0.0")) + ) assert(ScalafmtVersion.parse("avs") == Left(InvalidVersionException("avs"))) - assert(ScalafmtVersion.parse("1.2.3-M14") == Left(InvalidVersionException("1.2.3-M14"))) + assert( + ScalafmtVersion + .parse("1.2.3-M14") == Left(InvalidVersionException("1.2.3-M14")) + ) } test("order versions") { assert(ScalafmtVersion(2, 0, 0, 0) > ScalafmtVersion(1, 5, 1, 0)) + assert(ScalafmtVersion(2, 0, 0, 0) > ScalafmtVersion(2, 0, 0, 1)) + assert(ScalafmtVersion(2, 0, 0, 4) > ScalafmtVersion(1, 9, 9, 9)) assert(ScalafmtVersion(0, 1, 2, 0) > ScalafmtVersion(0, 1, 1, 0)) assert(ScalafmtVersion(0, 2, 2, 0) > ScalafmtVersion(0, 1, 2, 0)) - assert(ScalafmtVersion(2, 0, 0, 0) < ScalafmtVersion(2, 0, 0, 4)) + assert(ScalafmtVersion(2, 0, 0, 2) < ScalafmtVersion(2, 0, 0, 4)) + assert(ScalafmtVersion(2, 0, 0, 2) < ScalafmtVersion(2, 0, 0, 0)) assert(ScalafmtVersion(0, 1, 2, 0) < ScalafmtVersion(1, 0, 0, 0)) assert(ScalafmtVersion(0, 1, 8, 0) < ScalafmtVersion(0, 2, 2, 0)) - // check false positives assert(!(ScalafmtVersion(2, 0, 0, 0) < ScalafmtVersion(1, 5, 1, 0))) assert(!(ScalafmtVersion(0, 1, 8, 0) > ScalafmtVersion(0, 2, 2, 0))) } From 48ddad03cd1ecc08f405ec4bf7b7abdb09210b10 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Wed, 16 Oct 2019 19:02:02 +0300 Subject: [PATCH 16/18] Reverted travis CI cache --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c15a0c2ebb..1e13d7dd7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,8 +34,7 @@ jobs: cache: directories: - - $HOME/.sbt/1.0/dependency - - $HOME/.sbt/launchers + - $HOME/.sbt - $HOME/.ivy2/cache - $HOME/.coursier - target/repos From 82e36e01b11e46bc0bdb6f2bf5b9d8c910c03b93 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Wed, 16 Oct 2019 19:02:32 +0300 Subject: [PATCH 17/18] Changed versions in ScalafmtDynamicDownloader --- .../main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala | 2 +- .../org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala index ed2a453226..a26e35ec74 100644 --- a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala +++ b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamic.scala @@ -170,7 +170,7 @@ final case class ScalafmtDynamic( case DownloadUnknownError(v, cause) => ScalafmtDynamicError.CannotDownload(configPath, v, Option(cause)) case InvalidVersionError(v, cause) => - ScalafmtDynamicError.CannotDownload(configPath, v, None) + ScalafmtDynamicError.CannotDownload(configPath, v, Option(cause)) } .flatMap(resolveClassPath(configPath, _)) } diff --git a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala index 64df043f6d..6782f6b49e 100644 --- a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala +++ b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtDynamicDownloader.scala @@ -70,14 +70,14 @@ class ScalafmtDynamicDownloader( @inline private def scalaBinaryVersion(version: ScalafmtVersion): String = - if (version < ScalafmtVersion(1, 0, 0, 0)) "2.11" - else if (version < ScalafmtVersion(2, 2, 0, 0)) "2.12" + if (version < ScalafmtVersion(0, 7, 0, 0)) "2.11" + else if (version < ScalafmtVersion(2, 1, 2, 0)) "2.12" else "2.13" @inline private def scalaVersion(version: ScalafmtVersion): String = - if (version < ScalafmtVersion(1, 0, 0, 0)) BuildInfo.scala211 - else if (version < ScalafmtVersion(2, 2, 0, 0)) BuildInfo.scala212 + if (version < ScalafmtVersion(0, 7, 0, 0)) BuildInfo.scala211 + else if (version < ScalafmtVersion(2, 1, 2, 0)) BuildInfo.scala212 else BuildInfo.scala @inline From 6dc2a22771db4c4e528c7dd9888e349bd0057c72 Mon Sep 17 00:00:00 2001 From: Mikhail Chugunkov Date: Wed, 16 Oct 2019 19:02:43 +0300 Subject: [PATCH 18/18] ScalafmtVersion rewrote into regex --- .../scalafmt/dynamic/ScalafmtVersion.scala | 42 +++++++++++++------ .../scala/tests/ScalafmtVersionSuite.scala | 27 ++++++++++++ 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala index 42b364c0f9..ad632cc7db 100644 --- a/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala +++ b/scalafmt-dynamic/src/main/scala/org/scalafmt/dynamic/ScalafmtVersion.scala @@ -3,7 +3,7 @@ package org.scalafmt.dynamic import scala.util.control.{NonFatal, NoStackTrace} case class ScalafmtVersion(major: Int, minor: Int, patch: Int, rc: Int) { - private lazy val integerRepr: Int = + private val integerRepr: Int = major * 100 + minor * 10 + patch def <(other: ScalafmtVersion): Boolean = @@ -22,20 +22,38 @@ object ScalafmtVersion { extends Exception(s"Invalid scalafmt version $version") with NoStackTrace + private val versionRegex = """(\d)\.(\d)\.(\d)(-RC(\d))?""".r + def parse(version: String): Either[InvalidVersionException, ScalafmtVersion] = try { - val splitByDot = version.split("\\.") - val major = splitByDot(0).toInt - val minor = splitByDot(1).toInt - val splitByHyphen = splitByDot(2).split("-") - val patch = splitByHyphen(0).toInt - val rc = - if (splitByHyphen.size == 1) 0 - else if (splitByHyphen(1).startsWith("RC")) - splitByHyphen(1).drop(2).toInt - else throw InvalidVersionException(version) - Right(ScalafmtVersion(major, minor, patch, rc)) + version match { + case versionRegex(major, minor, patch, null, null) => + Right( + ScalafmtVersion( + positiveInt(major), + positiveInt(minor), + positiveInt(patch), + 0 + ) + ) + case versionRegex(major, minor, patch, _, rc) => + Right( + ScalafmtVersion( + positiveInt(major), + positiveInt(minor), + positiveInt(patch), + positiveInt(rc) + ) + ) + case _ => Left(InvalidVersionException(version)) + } } catch { case e if NonFatal(e) => Left(InvalidVersionException(version)) } + + private def positiveInt(s: String): Int = { + val i = s.toInt + require(i >= 0) + i + } } diff --git a/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala b/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala index 163e3db516..9c9719c990 100644 --- a/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala +++ b/scalafmt-dynamic/src/test/scala/tests/ScalafmtVersionSuite.scala @@ -24,6 +24,33 @@ class ScalafmtVersionSuite extends FunSuite { ScalafmtVersion .parse("1.2.3-M14") == Left(InvalidVersionException("1.2.3-M14")) ) + assert( + ScalafmtVersion + .parse("1.1.1.1") == Left(InvalidVersionException("1.1.1.1")) + ) + assert( + ScalafmtVersion.parse("2.-1.0") == Left(InvalidVersionException("2.-1.0")) + ) + assert( + ScalafmtVersion.parse("2.1.0.") == Left(InvalidVersionException("2.1.0.")) + ) + assert( + ScalafmtVersion.parse(",2.1.0") == Left(InvalidVersionException(",2.1.0")) + ) + assert( + ScalafmtVersion.parse("2.1a.0") == Left(InvalidVersionException("2.1a.0")) + ) + assert( + ScalafmtVersion.parse("2.1.0-") == Left(InvalidVersionException("2.1.0-")) + ) + assert( + ScalafmtVersion + .parse("2.1.0-rc1") == Left(InvalidVersionException("2.1.0-rc1")) + ) + assert( + ScalafmtVersion + .parse("2.1.0-RC1-M4") == Left(InvalidVersionException("2.1.0-RC1-M4")) + ) } test("order versions") {