From 58956d1c581424111a64381be983211c38f972e5 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 23 Jun 2017 17:54:59 +0100 Subject: [PATCH 01/50] Drop util-apply-macro dep, given it was brought back --- project/Dependencies.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ada99b2bb..36af878d7 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -19,7 +19,6 @@ object Dependencies { private val sbtIO = "org.scala-sbt" %% "io" % ioVersion - private val utilApplyMacro = "org.scala-sbt" %% "util-apply-macro" % utilVersion private val utilCache = "org.scala-sbt" %% "util-cache" % utilVersion private val utilCollection = "org.scala-sbt" %% "util-collection" % utilVersion private val utilCompletion = "org.scala-sbt" %% "util-completion" % utilVersion @@ -72,8 +71,6 @@ object Dependencies { def addSbtIO(p: Project): Project = addSbtModule(p, sbtIoPath, "io", sbtIO) - def addSbtUtilApplyMacro(p: Project): Project = - addSbtModule(p, sbtUtilPath, "utilApplyMacro", utilApplyMacro) def addSbtUtilCache(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilCache", utilCache) def addSbtUtilCollection(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilCollection", utilCollection) From f63306140e9dc51cb4e6d8cf93737f27b82b06f0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 23 Jun 2017 17:58:00 +0100 Subject: [PATCH 02/50] Format the build --- build.sbt | 66 ++++++++++++++++++++------------------ project/Dependencies.scala | 12 ++++--- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/build.sbt b/build.sbt index 6f6711f38..8bd3aa316 100644 --- a/build.sbt +++ b/build.sbt @@ -247,12 +247,14 @@ lazy val commandProj = (project in file("main-command")) sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats ) - .configure(addSbtCompilerInterface, - addSbtIO, - addSbtUtilLogging, - addSbtUtilCompletion, - addSbtCompilerClasspath, - addSbtLm) + .configure( + addSbtCompilerInterface, + addSbtIO, + addSbtUtilLogging, + addSbtUtilCompletion, + addSbtCompilerClasspath, + addSbtLm + ) // The core macro project defines the main logic of the DSL, abstracted // away from several sbt implementators (tasks, settings, et cetera). @@ -315,12 +317,14 @@ lazy val mainProj = (project in file("main")) baseDirectory.value / "src" / "main" / "contraband-scala", sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala" ) - .configure(addSbtCompilerInterface, - addSbtIO, - addSbtUtilLogging, - addSbtUtilLogic, - addSbtLm, - addSbtZincCompile) + .configure( + addSbtCompilerInterface, + addSbtIO, + addSbtUtilLogging, + addSbtUtilLogic, + addSbtLm, + addSbtZincCompile + ) // Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object // technically, we need a dependency on all of mainProj's dependencies, but we don't do that since this is strictly an integration project @@ -406,16 +410,14 @@ def otherRootSettings = aggregate in bintrayRelease := false ) ++ inConfig(Scripted.RepoOverrideTest)( Seq( - scriptedPrescripted := { _ => - () - }, - scriptedLaunchOpts := { - List("-Xmx1500M", - "-Xms512M", - "-server", - "-Dsbt.override.build.repos=true", - s"""-Dsbt.repository.config=${scriptedSource.value / "repo.config"}""") - }, + scriptedPrescripted := (_ => ()), + scriptedLaunchOpts := List( + "-Xmx1500M", + "-Xms512M", + "-server", + "-Dsbt.override.build.repos=true", + s"""-Dsbt.repository.config=${scriptedSource.value / "repo.config"}""" + ), scripted := scriptedTask.evaluated, scriptedUnpublished := scriptedUnpublishedTask.evaluated, scriptedSource := (sourceDirectory in sbtProj).value / "repo-override-test" @@ -449,15 +451,17 @@ lazy val safeProjects: ScopeFilter = ScopeFilter( ) lazy val otherUnitTests = taskKey[Unit]("Unit test other projects") lazy val otherProjects: ScopeFilter = ScopeFilter( - inProjects(testingProj, - testAgentProj, - taskProj, - scriptedSbtProj, - scriptedPluginProj, - commandProj, - mainSettingsProj, - mainProj, - sbtProj), + inProjects( + testingProj, + testAgentProj, + taskProj, + scriptedSbtProj, + scriptedPluginProj, + commandProj, + mainSettingsProj, + mainProj, + sbtProj + ), inConfigurations(Test) ) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 36af878d7..bac99155a 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -57,11 +57,13 @@ object Dependencies { lazy val sbtLmPath = getSbtModulePath("sbtlm.path", "sbt/lm") lazy val sbtZincPath = getSbtModulePath("sbtzinc.path", "sbt/zinc") - def addSbtModule(p: Project, - path: Option[String], - projectName: String, - m: ModuleID, - c: Option[Configuration] = None) = + def addSbtModule( + p: Project, + path: Option[String], + projectName: String, + m: ModuleID, + c: Option[Configuration] = None + ) = path match { case Some(f) => p dependsOn c.fold[ClasspathDep[ProjectReference]](ProjectRef(file(f), projectName))( From 4cbad9eab1853beaa19df49080b5f44bc229c905 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 23 Jun 2017 17:45:36 +0100 Subject: [PATCH 03/50] Bring back collection, complete & logic Recovers these as they were at the time of their deletion: git checkout 4053ea92ba8b4978c583fe6ec5328843174a94e7 \ util/collection util/complete util/logic --- util/collection/NOTICE | 3 + .../collection/src/main/scala/sbt/AList.scala | 212 +++++ .../src/main/scala/sbt/Attributes.scala | 210 +++++ .../src/main/scala/sbt/Classes.scala | 24 + util/collection/src/main/scala/sbt/Dag.scala | 128 +++ .../collection/src/main/scala/sbt/HList.scala | 32 + .../collection/src/main/scala/sbt/IDSet.scala | 45 + .../collection/src/main/scala/sbt/INode.scala | 177 ++++ .../collection/src/main/scala/sbt/KList.scala | 53 ++ util/collection/src/main/scala/sbt/PMap.scala | 108 +++ .../collection/src/main/scala/sbt/Param.scala | 30 + .../src/main/scala/sbt/Positions.scala | 20 + .../src/main/scala/sbt/Settings.scala | 642 +++++++++++++ util/collection/src/main/scala/sbt/Show.scala | 8 + .../src/main/scala/sbt/ShowLines.scala | 15 + .../src/main/scala/sbt/Signal.scala | 85 ++ .../src/main/scala/sbt/TypeFunctions.scala | 50 ++ .../collection/src/main/scala/sbt/Types.scala | 12 + util/collection/src/main/scala/sbt/Util.scala | 43 + .../src/test/scala/DagSpecification.scala | 50 ++ util/collection/src/test/scala/KeyTest.scala | 32 + .../src/test/scala/LiteralTest.scala | 17 + util/collection/src/test/scala/PMapTest.scala | 18 + .../src/test/scala/SettingsExample.scala | 87 ++ .../src/test/scala/SettingsTest.scala | 195 ++++ util/complete/NOTICE | 3 + .../src/main/scala/sbt/LineReader.scala | 139 +++ .../main/scala/sbt/complete/Completions.scala | 144 +++ .../scala/sbt/complete/EditDistance.scala | 41 + .../scala/sbt/complete/ExampleSource.scala | 59 ++ .../src/main/scala/sbt/complete/History.scala | 45 + .../scala/sbt/complete/HistoryCommands.scala | 73 ++ .../scala/sbt/complete/JLineCompletion.scala | 156 ++++ .../src/main/scala/sbt/complete/Parser.scala | 848 ++++++++++++++++++ .../src/main/scala/sbt/complete/Parsers.scala | 268 ++++++ .../scala/sbt/complete/ProcessError.scala | 29 + .../scala/sbt/complete/TokenCompletions.scala | 37 + .../main/scala/sbt/complete/TypeString.scala | 79 ++ .../main/scala/sbt/complete/UpperBound.scala | 47 + util/complete/src/test/scala/ParserTest.scala | 148 +++ .../scala/sbt/complete/FileExamplesTest.scala | 89 ++ .../sbt/complete/FixedSetExamplesTest.scala | 26 + .../sbt/complete/ParserWithExamplesTest.scala | 93 ++ .../src/main/scala/sbt/logic/Logic.scala | 336 +++++++ .../logic/src/test/scala/sbt/logic/Test.scala | 115 +++ 45 files changed, 5071 insertions(+) create mode 100644 util/collection/NOTICE create mode 100644 util/collection/src/main/scala/sbt/AList.scala create mode 100644 util/collection/src/main/scala/sbt/Attributes.scala create mode 100644 util/collection/src/main/scala/sbt/Classes.scala create mode 100644 util/collection/src/main/scala/sbt/Dag.scala create mode 100644 util/collection/src/main/scala/sbt/HList.scala create mode 100644 util/collection/src/main/scala/sbt/IDSet.scala create mode 100644 util/collection/src/main/scala/sbt/INode.scala create mode 100644 util/collection/src/main/scala/sbt/KList.scala create mode 100644 util/collection/src/main/scala/sbt/PMap.scala create mode 100644 util/collection/src/main/scala/sbt/Param.scala create mode 100755 util/collection/src/main/scala/sbt/Positions.scala create mode 100644 util/collection/src/main/scala/sbt/Settings.scala create mode 100644 util/collection/src/main/scala/sbt/Show.scala create mode 100644 util/collection/src/main/scala/sbt/ShowLines.scala create mode 100644 util/collection/src/main/scala/sbt/Signal.scala create mode 100644 util/collection/src/main/scala/sbt/TypeFunctions.scala create mode 100644 util/collection/src/main/scala/sbt/Types.scala create mode 100644 util/collection/src/main/scala/sbt/Util.scala create mode 100644 util/collection/src/test/scala/DagSpecification.scala create mode 100644 util/collection/src/test/scala/KeyTest.scala create mode 100644 util/collection/src/test/scala/LiteralTest.scala create mode 100644 util/collection/src/test/scala/PMapTest.scala create mode 100644 util/collection/src/test/scala/SettingsExample.scala create mode 100644 util/collection/src/test/scala/SettingsTest.scala create mode 100644 util/complete/NOTICE create mode 100644 util/complete/src/main/scala/sbt/LineReader.scala create mode 100644 util/complete/src/main/scala/sbt/complete/Completions.scala create mode 100644 util/complete/src/main/scala/sbt/complete/EditDistance.scala create mode 100644 util/complete/src/main/scala/sbt/complete/ExampleSource.scala create mode 100644 util/complete/src/main/scala/sbt/complete/History.scala create mode 100644 util/complete/src/main/scala/sbt/complete/HistoryCommands.scala create mode 100644 util/complete/src/main/scala/sbt/complete/JLineCompletion.scala create mode 100644 util/complete/src/main/scala/sbt/complete/Parser.scala create mode 100644 util/complete/src/main/scala/sbt/complete/Parsers.scala create mode 100644 util/complete/src/main/scala/sbt/complete/ProcessError.scala create mode 100644 util/complete/src/main/scala/sbt/complete/TokenCompletions.scala create mode 100644 util/complete/src/main/scala/sbt/complete/TypeString.scala create mode 100644 util/complete/src/main/scala/sbt/complete/UpperBound.scala create mode 100644 util/complete/src/test/scala/ParserTest.scala create mode 100644 util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala create mode 100644 util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala create mode 100644 util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala create mode 100644 util/logic/src/main/scala/sbt/logic/Logic.scala create mode 100644 util/logic/src/test/scala/sbt/logic/Test.scala diff --git a/util/collection/NOTICE b/util/collection/NOTICE new file mode 100644 index 000000000..428020987 --- /dev/null +++ b/util/collection/NOTICE @@ -0,0 +1,3 @@ +Simple Build Tool: Collection Component +Copyright 2010 Mark Harrah +Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/AList.scala b/util/collection/src/main/scala/sbt/AList.scala new file mode 100644 index 000000000..24368219b --- /dev/null +++ b/util/collection/src/main/scala/sbt/AList.scala @@ -0,0 +1,212 @@ +package sbt + +import Classes.Applicative +import Types._ + +/** + * An abstraction over a higher-order type constructor `K[x[y]]` with the purpose of abstracting + * over heterogeneous sequences like `KList` and `TupleN` with elements with a common type + * constructor as well as homogeneous sequences `Seq[M[T]]`. + */ +trait AList[K[L[x]]] { + def transform[M[_], N[_]](value: K[M], f: M ~> N): K[N] + def traverse[M[_], N[_], P[_]](value: K[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[K[P]] + def foldr[M[_], A](value: K[M], f: (M[_], A) => A, init: A): A + + def toList[M[_]](value: K[M]): List[M[_]] = foldr[M, List[M[_]]](value, _ :: _, Nil) + def apply[M[_], C](value: K[M], f: K[Id] => C)(implicit a: Applicative[M]): M[C] = + a.map(f, traverse[M, M, Id](value, idK[M])(a)) +} +object AList { + type Empty = AList[({ type l[L[x]] = Unit })#l] + /** AList for Unit, which represents a sequence that is always empty.*/ + val empty: Empty = new Empty { + def transform[M[_], N[_]](in: Unit, f: M ~> N) = () + def foldr[M[_], T](in: Unit, f: (M[_], T) => T, init: T) = init + override def apply[M[_], C](in: Unit, f: Unit => C)(implicit app: Applicative[M]): M[C] = app.pure(f(())) + def traverse[M[_], N[_], P[_]](in: Unit, f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Unit] = np.pure(()) + } + + type SeqList[T] = AList[({ type l[L[x]] = List[L[T]] })#l] + /** AList for a homogeneous sequence. */ + def seq[T]: SeqList[T] = new SeqList[T] { + def transform[M[_], N[_]](s: List[M[T]], f: M ~> N) = s.map(f.fn[T]) + def foldr[M[_], A](s: List[M[T]], f: (M[_], A) => A, init: A): A = (init /: s.reverse)((t, m) => f(m, t)) + override def apply[M[_], C](s: List[M[T]], f: List[T] => C)(implicit ap: Applicative[M]): M[C] = + { + def loop[V](in: List[M[T]], g: List[T] => V): M[V] = + in match { + case Nil => ap.pure(g(Nil)) + case x :: xs => + val h = (ts: List[T]) => (t: T) => g(t :: ts) + ap.apply(loop(xs, h), x) + } + loop(s, f) + } + def traverse[M[_], N[_], P[_]](s: List[M[T]], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[List[P[T]]] = ??? + } + + /** AList for the arbitrary arity data structure KList. */ + def klist[KL[M[_]] <: KList[M] { type Transform[N[_]] = KL[N] }]: AList[KL] = new AList[KL] { + def transform[M[_], N[_]](k: KL[M], f: M ~> N) = k.transform(f) + def foldr[M[_], T](k: KL[M], f: (M[_], T) => T, init: T): T = k.foldr(f, init) + override def apply[M[_], C](k: KL[M], f: KL[Id] => C)(implicit app: Applicative[M]): M[C] = k.apply(f)(app) + def traverse[M[_], N[_], P[_]](k: KL[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KL[P]] = k.traverse[N, P](f)(np) + override def toList[M[_]](k: KL[M]) = k.toList + } + + /** AList for a single value. */ + type Single[A] = AList[({ type l[L[x]] = L[A] })#l] + def single[A]: Single[A] = new Single[A] { + def transform[M[_], N[_]](a: M[A], f: M ~> N) = f(a) + def foldr[M[_], T](a: M[A], f: (M[_], T) => T, init: T): T = f(a, init) + def traverse[M[_], N[_], P[_]](a: M[A], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[P[A]] = f(a) + } + + type ASplit[K[L[x]], B[x]] = AList[({ type l[L[x]] = K[(L ∙ B)#l] })#l] + /** AList that operates on the outer type constructor `A` of a composition `[x] A[B[x]]` for type constructors `A` and `B`*/ + def asplit[K[L[x]], B[x]](base: AList[K]): ASplit[K, B] = new ASplit[K, B] { + type Split[L[x]] = K[(L ∙ B)#l] + def transform[M[_], N[_]](value: Split[M], f: M ~> N): Split[N] = + base.transform[(M ∙ B)#l, (N ∙ B)#l](value, nestCon[M, N, B](f)) + + def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Split[P]] = + { + val g = nestCon[M, (N ∙ P)#l, B](f) + base.traverse[(M ∙ B)#l, N, (P ∙ B)#l](value, g)(np) + } + + def foldr[M[_], A](value: Split[M], f: (M[_], A) => A, init: A): A = + base.foldr[(M ∙ B)#l, A](value, f, init) + } + + // TODO: auto-generate + sealed trait T2K[A, B] { type l[L[x]] = (L[A], L[B]) } + type T2List[A, B] = AList[T2K[A, B]#l] + def tuple2[A, B]: T2List[A, B] = new T2List[A, B] { + type T2[M[_]] = (M[A], M[B]) + def transform[M[_], N[_]](t: T2[M], f: M ~> N): T2[N] = (f(t._1), f(t._2)) + def foldr[M[_], T](t: T2[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, init)) + def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T2[P]] = + { + val g = (Tuple2.apply[P[A], P[B]] _).curried + np.apply(np.map(g, f(t._1)), f(t._2)) + } + } + + sealed trait T3K[A, B, C] { type l[L[x]] = (L[A], L[B], L[C]) } + type T3List[A, B, C] = AList[T3K[A, B, C]#l] + def tuple3[A, B, C]: T3List[A, B, C] = new T3List[A, B, C] { + type T3[M[_]] = (M[A], M[B], M[C]) + def transform[M[_], N[_]](t: T3[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3)) + def foldr[M[_], T](t: T3[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, init))) + def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T3[P]] = + { + val g = (Tuple3.apply[P[A], P[B], P[C]] _).curried + np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)) + } + } + + sealed trait T4K[A, B, C, D] { type l[L[x]] = (L[A], L[B], L[C], L[D]) } + type T4List[A, B, C, D] = AList[T4K[A, B, C, D]#l] + def tuple4[A, B, C, D]: T4List[A, B, C, D] = new T4List[A, B, C, D] { + type T4[M[_]] = (M[A], M[B], M[C], M[D]) + def transform[M[_], N[_]](t: T4[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4)) + def foldr[M[_], T](t: T4[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, init)))) + def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T4[P]] = + { + val g = (Tuple4.apply[P[A], P[B], P[C], P[D]] _).curried + np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)) + } + } + + sealed trait T5K[A, B, C, D, E] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E]) } + type T5List[A, B, C, D, E] = AList[T5K[A, B, C, D, E]#l] + def tuple5[A, B, C, D, E]: T5List[A, B, C, D, E] = new T5List[A, B, C, D, E] { + type T5[M[_]] = (M[A], M[B], M[C], M[D], M[E]) + def transform[M[_], N[_]](t: T5[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5)) + def foldr[M[_], T](t: T5[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, init))))) + def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T5[P]] = + { + val g = (Tuple5.apply[P[A], P[B], P[C], P[D], P[E]] _).curried + np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)) + } + } + + sealed trait T6K[A, B, C, D, E, F] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F]) } + type T6List[A, B, C, D, E, F] = AList[T6K[A, B, C, D, E, F]#l] + def tuple6[A, B, C, D, E, F]: T6List[A, B, C, D, E, F] = new T6List[A, B, C, D, E, F] { + type T6[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F]) + def transform[M[_], N[_]](t: T6[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6)) + def foldr[M[_], T](t: T6[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, init)))))) + def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T6[P]] = + { + val g = (Tuple6.apply[P[A], P[B], P[C], P[D], P[E], P[F]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)) + } + } + + sealed trait T7K[A, B, C, D, E, F, G] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G]) } + type T7List[A, B, C, D, E, F, G] = AList[T7K[A, B, C, D, E, F, G]#l] + def tuple7[A, B, C, D, E, F, G]: T7List[A, B, C, D, E, F, G] = new T7List[A, B, C, D, E, F, G] { + type T7[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G]) + def transform[M[_], N[_]](t: T7[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7)) + def foldr[M[_], T](t: T7[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, init))))))) + def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T7[P]] = + { + val g = (Tuple7.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)) + } + } + sealed trait T8K[A, B, C, D, E, F, G, H] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H]) } + type T8List[A, B, C, D, E, F, G, H] = AList[T8K[A, B, C, D, E, F, G, H]#l] + def tuple8[A, B, C, D, E, F, G, H]: T8List[A, B, C, D, E, F, G, H] = new T8List[A, B, C, D, E, F, G, H] { + type T8[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H]) + def transform[M[_], N[_]](t: T8[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8)) + def foldr[M[_], T](t: T8[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, init)))))))) + def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T8[P]] = + { + val g = (Tuple8.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)) + } + } + + sealed trait T9K[A, B, C, D, E, F, G, H, I] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I]) } + type T9List[A, B, C, D, E, F, G, H, I] = AList[T9K[A, B, C, D, E, F, G, H, I]#l] + def tuple9[A, B, C, D, E, F, G, H, I]: T9List[A, B, C, D, E, F, G, H, I] = new T9List[A, B, C, D, E, F, G, H, I] { + type T9[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I]) + def transform[M[_], N[_]](t: T9[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9)) + def foldr[M[_], T](t: T9[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, init))))))))) + def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T9[P]] = + { + val g = (Tuple9.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)) + } + } + + sealed trait T10K[A, B, C, D, E, F, G, H, I, J] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J]) } + type T10List[A, B, C, D, E, F, G, H, I, J] = AList[T10K[A, B, C, D, E, F, G, H, I, J]#l] + def tuple10[A, B, C, D, E, F, G, H, I, J]: T10List[A, B, C, D, E, F, G, H, I, J] = new T10List[A, B, C, D, E, F, G, H, I, J] { + type T10[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J]) + def transform[M[_], N[_]](t: T10[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10)) + def foldr[M[_], T](t: T10[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, init)))))))))) + def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T10[P]] = + { + val g = (Tuple10.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)) + } + } + + sealed trait T11K[A, B, C, D, E, F, G, H, I, J, K] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J], L[K]) } + type T11List[A, B, C, D, E, F, G, H, I, J, K] = AList[T11K[A, B, C, D, E, F, G, H, I, J, K]#l] + def tuple11[A, B, C, D, E, F, G, H, I, J, K]: T11List[A, B, C, D, E, F, G, H, I, J, K] = new T11List[A, B, C, D, E, F, G, H, I, J, K] { + type T11[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J], M[K]) + def transform[M[_], N[_]](t: T11[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10), f(t._11)) + def foldr[M[_], T](t: T11[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, f(t._11, init))))))))))) + def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T11[P]] = + { + val g = (Tuple11.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J], P[K]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)), f(t._11)) + } + } +} diff --git a/util/collection/src/main/scala/sbt/Attributes.scala b/util/collection/src/main/scala/sbt/Attributes.scala new file mode 100644 index 000000000..64f379012 --- /dev/null +++ b/util/collection/src/main/scala/sbt/Attributes.scala @@ -0,0 +1,210 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +import Types._ +import scala.reflect.Manifest + +// T must be invariant to work properly. +// Because it is sealed and the only instances go through AttributeKey.apply, +// a single AttributeKey instance cannot conform to AttributeKey[T] for different Ts + +/** + * A key in an [[AttributeMap]] that constrains its associated value to be of type `T`. + * The key is uniquely defined by its [[label]] and type `T`, represented at runtime by [[manifest]]. + */ +sealed trait AttributeKey[T] { + + /** The runtime evidence for `T` */ + def manifest: Manifest[T] + + @deprecated("Should only be used for compatibility during the transition from hyphenated labels to camelCase labels.", "0.13.0") + def rawLabel: String + + /** The label is the identifier for the key and is camelCase by convention. */ + def label: String + + /** An optional, brief description of the key. */ + def description: Option[String] + + /** + * In environments that support delegation, looking up this key when it has no associated value will delegate to the values associated with these keys. + * The delegation proceeds in order the keys are returned here. + */ + def extend: Seq[AttributeKey[_]] + + /** + * Specifies whether this key is a local, anonymous key (`true`) or not (`false`). + * This is typically only used for programmatic, intermediate keys that should not be referenced outside of a specific scope. + */ + def isLocal: Boolean + + /** Identifies the relative importance of a key among other keys.*/ + def rank: Int +} +private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] { + override final def toString = label + override final def hashCode = label.hashCode + override final def equals(o: Any) = (this eq o.asInstanceOf[AnyRef]) || (o match { + case a: SharedAttributeKey[t] => a.label == this.label && a.manifest == this.manifest + case _ => false + }) + final def isLocal: Boolean = false +} +object AttributeKey { + def apply[T](name: String)(implicit mf: Manifest[T]): AttributeKey[T] = + make(name, None, Nil, Int.MaxValue) + + def apply[T](name: String, rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = + make(name, None, Nil, rank) + + def apply[T](name: String, description: String)(implicit mf: Manifest[T]): AttributeKey[T] = + apply(name, description, Nil) + + def apply[T](name: String, description: String, rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = + apply(name, description, Nil, rank) + + def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]])(implicit mf: Manifest[T]): AttributeKey[T] = + apply(name, description, extend, Int.MaxValue) + + def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]], rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = + make(name, Some(description), extend, rank) + + private[this] def make[T](name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int)(implicit mf: Manifest[T]): AttributeKey[T] = new SharedAttributeKey[T] { + def manifest = mf + def rawLabel = name + val label = Util.hyphenToCamel(name) + def description = description0 + def extend = extend0 + def rank = rank0 + } + private[sbt] def local[T](implicit mf: Manifest[T]): AttributeKey[T] = new AttributeKey[T] { + def manifest = mf + def rawLabel = LocalLabel + def label = LocalLabel + def description = None + def extend = Nil + override def toString = label + def isLocal: Boolean = true + def rank = Int.MaxValue + } + private[sbt] final val LocalLabel = "$local" +} + +/** + * An immutable map where a key is the tuple `(String,T)` for a fixed type `T` and can only be associated with values of type `T`. + * It is therefore possible for this map to contain mappings for keys with the same label but different types. + * Excluding this possibility is the responsibility of the client if desired. + */ +trait AttributeMap { + /** + * Gets the value of type `T` associated with the key `k`. + * If a key with the same label but different type is defined, this method will fail. + */ + def apply[T](k: AttributeKey[T]): T + + /** + * Gets the value of type `T` associated with the key `k` or `None` if no value is associated. + * If a key with the same label but a different type is defined, this method will return `None`. + */ + def get[T](k: AttributeKey[T]): Option[T] + + /** + * Returns this map without the mapping for `k`. + * This method will not remove a mapping for a key with the same label but a different type. + */ + def remove[T](k: AttributeKey[T]): AttributeMap + + /** + * Returns true if this map contains a mapping for `k`. + * If a key with the same label but a different type is defined in this map, this method will return `false`. + */ + def contains[T](k: AttributeKey[T]): Boolean + + /** + * Adds the mapping `k -> value` to this map, replacing any existing mapping for `k`. + * Any mappings for keys with the same label but different types are unaffected. + */ + def put[T](k: AttributeKey[T], value: T): AttributeMap + + /** All keys with defined mappings. There may be multiple keys with the same `label`, but different types. */ + def keys: Iterable[AttributeKey[_]] + + /** Adds the mappings in `o` to this map, with mappings in `o` taking precedence over existing mappings.*/ + def ++(o: Iterable[AttributeEntry[_]]): AttributeMap + + /** Combines the mappings in `o` with the mappings in this map, with mappings in `o` taking precedence over existing mappings.*/ + def ++(o: AttributeMap): AttributeMap + + /** All mappings in this map. The [[AttributeEntry]] type preserves the typesafety of mappings, although the specific types are unknown.*/ + def entries: Iterable[AttributeEntry[_]] + + /** `true` if there are no mappings in this map, `false` if there are. */ + def isEmpty: Boolean +} +object AttributeMap { + /** An [[AttributeMap]] without any mappings. */ + val empty: AttributeMap = new BasicAttributeMap(Map.empty) + + /** Constructs an [[AttributeMap]] containing the given `entries`. */ + def apply(entries: Iterable[AttributeEntry[_]]): AttributeMap = empty ++ entries + + /** Constructs an [[AttributeMap]] containing the given `entries`.*/ + def apply(entries: AttributeEntry[_]*): AttributeMap = empty ++ entries + + /** Presents an `AttributeMap` as a natural transformation. */ + implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = new (AttributeKey ~> Id) { + def apply[T](key: AttributeKey[T]): T = map(key) + } +} +private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any]) extends AttributeMap { + def isEmpty: Boolean = backing.isEmpty + def apply[T](k: AttributeKey[T]) = backing(k).asInstanceOf[T] + def get[T](k: AttributeKey[T]) = backing.get(k).asInstanceOf[Option[T]] + def remove[T](k: AttributeKey[T]): AttributeMap = new BasicAttributeMap(backing - k) + def contains[T](k: AttributeKey[T]) = backing.contains(k) + def put[T](k: AttributeKey[T], value: T): AttributeMap = new BasicAttributeMap(backing.updated(k, value)) + def keys: Iterable[AttributeKey[_]] = backing.keys + def ++(o: Iterable[AttributeEntry[_]]): AttributeMap = + { + val newBacking = (backing /: o) { case (b, AttributeEntry(key, value)) => b.updated(key, value) } + new BasicAttributeMap(newBacking) + } + def ++(o: AttributeMap): AttributeMap = + o match { + case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing) + case _ => o ++ this + } + def entries: Iterable[AttributeEntry[_]] = + for ((k: AttributeKey[kt], v) <- backing) yield AttributeEntry(k, v.asInstanceOf[kt]) + override def toString = entries.mkString("(", ", ", ")") +} + +// type inference required less generality +/** A map entry where `key` is constrained to only be associated with a fixed value of type `T`. */ +final case class AttributeEntry[T](key: AttributeKey[T], value: T) { + override def toString = key.label + ": " + value +} + +/** Associates a `metadata` map with `data`. */ +final case class Attributed[D](data: D)(val metadata: AttributeMap) { + /** Retrieves the associated value of `key` from the metadata. */ + def get[T](key: AttributeKey[T]): Option[T] = metadata.get(key) + + /** Defines a mapping `key -> value` in the metadata. */ + def put[T](key: AttributeKey[T], value: T): Attributed[D] = Attributed(data)(metadata.put(key, value)) + + /** Transforms the data by applying `f`. */ + def map[T](f: D => T): Attributed[T] = Attributed(f(data))(metadata) +} +object Attributed { + /** Extracts the underlying data from the sequence `in`. */ + def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data) + + /** Associates empty metadata maps with each entry of `in`.*/ + def blankSeq[T](in: Seq[T]): Seq[Attributed[T]] = in map blank + + /** Associates an empty metadata map with `data`. */ + def blank[T](data: T): Attributed[T] = Attributed(data)(AttributeMap.empty) +} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/Classes.scala b/util/collection/src/main/scala/sbt/Classes.scala new file mode 100644 index 000000000..1db644f96 --- /dev/null +++ b/util/collection/src/main/scala/sbt/Classes.scala @@ -0,0 +1,24 @@ +package sbt + +object Classes { + trait Applicative[M[_]] { + def apply[S, T](f: M[S => T], v: M[S]): M[T] + def pure[S](s: => S): M[S] + def map[S, T](f: S => T, v: M[S]): M[T] + } + trait Monad[M[_]] extends Applicative[M] { + def flatten[T](m: M[M[T]]): M[T] + } + implicit val optionMonad: Monad[Option] = new Monad[Option] { + def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match { case (Some(fv), Some(vv)) => Some(fv(vv)); case _ => None } + def pure[S](s: => S) = Some(s) + def map[S, T](f: S => T, v: Option[S]) = v map f + def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten + } + implicit val listMonad: Monad[List] = new Monad[List] { + def apply[S, T](f: List[S => T], v: List[S]) = for (fv <- f; vv <- v) yield fv(vv) + def pure[S](s: => S) = s :: Nil + def map[S, T](f: S => T, v: List[S]) = v map f + def flatten[T](m: List[List[T]]): List[T] = m.flatten + } +} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/Dag.scala b/util/collection/src/main/scala/sbt/Dag.scala new file mode 100644 index 000000000..118cd0dff --- /dev/null +++ b/util/collection/src/main/scala/sbt/Dag.scala @@ -0,0 +1,128 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 David MacIver, Mark Harrah + */ +package sbt; + +trait Dag[Node <: Dag[Node]] { + self: Node => + + def dependencies: Iterable[Node] + def topologicalSort = Dag.topologicalSort(self)(_.dependencies) +} +object Dag { + import scala.collection.{ mutable, JavaConverters } + import JavaConverters.asScalaSetConverter + + def topologicalSort[T](root: T)(dependencies: T => Iterable[T]): List[T] = topologicalSort(root :: Nil)(dependencies) + + def topologicalSort[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = + { + val discovered = new mutable.HashSet[T] + val finished = (new java.util.LinkedHashSet[T]).asScala + + def visitAll(nodes: Iterable[T]) = nodes foreach visit + def visit(node: T): Unit = { + if (!discovered(node)) { + discovered(node) = true; + try { visitAll(dependencies(node)); } catch { case c: Cyclic => throw node :: c } + finished += node + } else if (!finished(node)) + throw new Cyclic(node) + } + + visitAll(nodes) + + finished.toList + } + // doesn't check for cycles + def topologicalSortUnchecked[T](node: T)(dependencies: T => Iterable[T]): List[T] = topologicalSortUnchecked(node :: Nil)(dependencies) + + def topologicalSortUnchecked[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = + { + val discovered = new mutable.HashSet[T] + var finished: List[T] = Nil + + def visitAll(nodes: Iterable[T]) = nodes foreach visit + def visit(node: T): Unit = { + if (!discovered(node)) { + discovered(node) = true + visitAll(dependencies(node)) + finished ::= node + } + } + + visitAll(nodes); + finished; + } + final class Cyclic(val value: Any, val all: List[Any], val complete: Boolean) + extends Exception("Cyclic reference involving " + + (if (complete) all.mkString("\n ", "\n ", "") else value) + ) { + def this(value: Any) = this(value, value :: Nil, false) + override def toString = getMessage + def ::(a: Any): Cyclic = + if (complete) + this + else if (a == value) + new Cyclic(value, all, true) + else + new Cyclic(value, a :: all, false) + } + + /** A directed graph with edges labeled positive or negative. */ + private[sbt] trait DirectedSignedGraph[Node] { + /** + * Directed edge type that tracks the sign and target (head) vertex. + * The sign can be obtained via [[isNegative]] and the target vertex via [[head]]. + */ + type Arrow + /** List of initial nodes. */ + def nodes: List[Arrow] + /** Outgoing edges for `n`. */ + def dependencies(n: Node): List[Arrow] + /** `true` if the edge `a` is "negative", false if it is "positive". */ + def isNegative(a: Arrow): Boolean + /** The target of the directed edge `a`. */ + def head(a: Arrow): Node + } + + /** + * Traverses a directed graph defined by `graph` looking for a cycle that includes a "negative" edge. + * The directed edges are weighted by the caller as "positive" or "negative". + * If a cycle containing a "negative" edge is detected, its member edges are returned in order. + * Otherwise, the empty list is returned. + */ + private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] = + { + import scala.annotation.tailrec + import graph._ + val finished = new mutable.HashSet[Node] + val visited = new mutable.HashSet[Node] + + def visit(edges: List[Arrow], stack: List[Arrow]): List[Arrow] = edges match { + case Nil => Nil + case edge :: tail => + val node = head(edge) + if (!visited(node)) { + visited += node + visit(dependencies(node), edge :: stack) match { + case Nil => + finished += node + visit(tail, stack) + case cycle => cycle + } + } else if (!finished(node)) { + // cycle. If a negative edge is involved, it is an error. + val between = edge :: stack.takeWhile(f => head(f) != node) + if (between exists isNegative) + between + else + visit(tail, stack) + } else + visit(tail, stack) + } + + visit(graph.nodes, Nil) + } + +} diff --git a/util/collection/src/main/scala/sbt/HList.scala b/util/collection/src/main/scala/sbt/HList.scala new file mode 100644 index 000000000..23f5488c6 --- /dev/null +++ b/util/collection/src/main/scala/sbt/HList.scala @@ -0,0 +1,32 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +import Types._ + +/** + * A minimal heterogeneous list type. For background, see + * http://apocalisp.wordpress.com/2010/07/06/type-level-programming-in-scala-part-6a-heterogeneous-list basics/ + */ +sealed trait HList { + type Wrap[M[_]] <: HList +} +sealed trait HNil extends HList { + type Wrap[M[_]] = HNil + def :+:[G](g: G): G :+: HNil = HCons(g, this) + + override def toString = "HNil" +} +object HNil extends HNil +final case class HCons[H, T <: HList](head: H, tail: T) extends HList { + type Wrap[M[_]] = M[H] :+: T#Wrap[M] + def :+:[G](g: G): G :+: H :+: T = HCons(g, this) + + override def toString = head + " :+: " + tail.toString +} + +object HList { + // contains no type information: not even A + implicit def fromList[A](list: Traversable[A]): HList = ((HNil: HList) /: list)((hl, v) => HCons(v, hl)) +} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/IDSet.scala b/util/collection/src/main/scala/sbt/IDSet.scala new file mode 100644 index 000000000..4f5245a26 --- /dev/null +++ b/util/collection/src/main/scala/sbt/IDSet.scala @@ -0,0 +1,45 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +/** A mutable set interface that uses object identity to test for set membership.*/ +trait IDSet[T] { + def apply(t: T): Boolean + def contains(t: T): Boolean + def +=(t: T): Unit + def ++=(t: Iterable[T]): Unit + def -=(t: T): Boolean + def all: collection.Iterable[T] + def toList: List[T] + def isEmpty: Boolean + def foreach(f: T => Unit): Unit + def process[S](t: T)(ifSeen: S)(ifNew: => S): S +} + +object IDSet { + implicit def toTraversable[T]: IDSet[T] => Traversable[T] = _.all + def apply[T](values: T*): IDSet[T] = apply(values) + def apply[T](values: Iterable[T]): IDSet[T] = + { + val s = create[T] + s ++= values + s + } + def create[T]: IDSet[T] = new IDSet[T] { + private[this] val backing = new java.util.IdentityHashMap[T, AnyRef] + private[this] val Dummy: AnyRef = "" + + def apply(t: T) = contains(t) + def contains(t: T) = backing.containsKey(t) + def foreach(f: T => Unit) = all foreach f + def +=(t: T) = backing.put(t, Dummy) + def ++=(t: Iterable[T]) = t foreach += + def -=(t: T) = if (backing.remove(t) eq null) false else true + def all = collection.JavaConversions.collectionAsScalaIterable(backing.keySet) + def toList = all.toList + def isEmpty = backing.isEmpty + def process[S](t: T)(ifSeen: S)(ifNew: => S) = if (contains(t)) ifSeen else { this += t; ifNew } + override def toString = backing.toString + } +} diff --git a/util/collection/src/main/scala/sbt/INode.scala b/util/collection/src/main/scala/sbt/INode.scala new file mode 100644 index 000000000..ce39fadad --- /dev/null +++ b/util/collection/src/main/scala/sbt/INode.scala @@ -0,0 +1,177 @@ +package sbt + +import java.lang.Runnable +import java.util.concurrent.{ atomic, Executor, LinkedBlockingQueue } +import atomic.{ AtomicBoolean, AtomicInteger } +import Types.{ :+:, ConstK, Id } + +object EvaluationState extends Enumeration { + val New, Blocked, Ready, Calling, Evaluated = Value +} + +abstract class EvaluateSettings[Scope] { + protected val init: Init[Scope] + import init._ + protected def executor: Executor + protected def compiledSettings: Seq[Compiled[_]] + + import EvaluationState.{ Value => EvaluationState, _ } + + private[this] val complete = new LinkedBlockingQueue[Option[Throwable]] + private[this] val static = PMap.empty[ScopedKey, INode] + private[this] val allScopes: Set[Scope] = compiledSettings.map(_.key.scope).toSet + private[this] def getStatic[T](key: ScopedKey[T]): INode[T] = static get key getOrElse sys.error("Illegal reference to key " + key) + + private[this] val transform: Initialize ~> INode = new (Initialize ~> INode) { + def apply[T](i: Initialize[T]): INode[T] = i match { + case k: Keyed[s, T] @unchecked => single(getStatic(k.scopedKey), k.transform) + case a: Apply[k, T] @unchecked => new MixedNode[k, T](a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist) + case b: Bind[s, T] @unchecked => new BindNode[s, T](transform(b.in), x => transform(b.f(x))) + case init.StaticScopes => strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope] + case v: Value[T] @unchecked => constant(v.value) + case v: ValidationCapture[T] @unchecked => strictConstant(v.key) + case t: TransformCapture => strictConstant(t.f) + case o: Optional[s, T] @unchecked => o.a match { + case None => constant(() => o.f(None)) + case Some(i) => single[s, T](transform(i), x => o.f(Some(x))) + } + } + } + private[this] lazy val roots: Seq[INode[_]] = compiledSettings flatMap { cs => + (cs.settings map { s => + val t = transform(s.init) + static(s.key) = t + t + }): Seq[INode[_]] + } + private[this] var running = new AtomicInteger + private[this] var cancel = new AtomicBoolean(false) + + def run(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = + { + assert(running.get() == 0, "Already running") + startWork() + roots.foreach(_.registerIfNew()) + workComplete() + complete.take() foreach { ex => + cancel.set(true) + throw ex + } + getResults(delegates) + } + private[this] def getResults(implicit delegates: Scope => Seq[Scope]) = + (empty /: static.toTypedSeq) { + case (ss, static.TPair(key, node)) => + if (key.key.isLocal) ss else ss.set(key.scope, key.key, node.get) + } + private[this] val getValue = new (INode ~> Id) { def apply[T](node: INode[T]) = node.get } + + private[this] def submitEvaluate(node: INode[_]) = submit(node.evaluate()) + private[this] def submitCallComplete[T](node: BindNode[_, T], value: T) = submit(node.callComplete(value)) + private[this] def submit(work: => Unit): Unit = + { + startWork() + executor.execute(new Runnable { def run = if (!cancel.get()) run0(work) }) + } + private[this] def run0(work: => Unit): Unit = + { + try { work } catch { case e: Throwable => complete.put(Some(e)) } + workComplete() + } + + private[this] def startWork(): Unit = running.incrementAndGet() + private[this] def workComplete(): Unit = + if (running.decrementAndGet() == 0) + complete.put(None) + + private[this] sealed abstract class INode[T] { + private[this] var state: EvaluationState = New + private[this] var value: T = _ + private[this] val blocking = new collection.mutable.ListBuffer[INode[_]] + private[this] var blockedOn: Int = 0 + private[this] val calledBy = new collection.mutable.ListBuffer[BindNode[_, T]] + + override def toString = getClass.getName + " (state=" + state + ",blockedOn=" + blockedOn + ",calledBy=" + calledBy.size + ",blocking=" + blocking.size + "): " + + keyString + + private[this] def keyString = + (static.toSeq.flatMap { case (key, value) => if (value eq this) init.showFullKey(key) :: Nil else Nil }).headOption getOrElse "non-static" + + final def get: T = synchronized { + assert(value != null, toString + " not evaluated") + value + } + final def doneOrBlock(from: INode[_]): Boolean = synchronized { + val ready = state == Evaluated + if (!ready) blocking += from + registerIfNew() + ready + } + final def isDone: Boolean = synchronized { state == Evaluated } + final def isNew: Boolean = synchronized { state == New } + final def isCalling: Boolean = synchronized { state == Calling } + final def registerIfNew(): Unit = synchronized { if (state == New) register() } + private[this] def register(): Unit = { + assert(state == New, "Already registered and: " + toString) + val deps = dependsOn + blockedOn = deps.size - deps.count(_.doneOrBlock(this)) + if (blockedOn == 0) + schedule() + else + state = Blocked + } + + final def schedule(): Unit = synchronized { + assert(state == New || state == Blocked, "Invalid state for schedule() call: " + toString) + state = Ready + submitEvaluate(this) + } + final def unblocked(): Unit = synchronized { + assert(state == Blocked, "Invalid state for unblocked() call: " + toString) + blockedOn -= 1 + assert(blockedOn >= 0, "Negative blockedOn: " + blockedOn + " for " + toString) + if (blockedOn == 0) schedule() + } + final def evaluate(): Unit = synchronized { evaluate0() } + protected final def makeCall(source: BindNode[_, T], target: INode[T]): Unit = { + assert(state == Ready, "Invalid state for call to makeCall: " + toString) + state = Calling + target.call(source) + } + protected final def setValue(v: T): Unit = { + assert(state != Evaluated, "Already evaluated (trying to set value to " + v + "): " + toString) + if (v == null) sys.error("Setting value cannot be null: " + keyString) + value = v + state = Evaluated + blocking foreach { _.unblocked() } + blocking.clear() + calledBy foreach { node => submitCallComplete(node, value) } + calledBy.clear() + } + final def call(by: BindNode[_, T]): Unit = synchronized { + registerIfNew() + state match { + case Evaluated => submitCallComplete(by, value) + case _ => calledBy += by + } + } + protected def dependsOn: Seq[INode[_]] + protected def evaluate0(): Unit + } + + private[this] def strictConstant[T](v: T): INode[T] = constant(() => v) + private[this] def constant[T](f: () => T): INode[T] = new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty) + private[this] def single[S, T](in: INode[S], f: S => T): INode[T] = new MixedNode[({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S]) + private[this] final class BindNode[S, T](in: INode[S], f: S => INode[T]) extends INode[T] { + protected def dependsOn = in :: Nil + protected def evaluate0(): Unit = makeCall(this, f(in.get)) + def callComplete(value: T): Unit = synchronized { + assert(isCalling, "Invalid state for callComplete(" + value + "): " + toString) + setValue(value) + } + } + private[this] final class MixedNode[K[L[x]], T](in: K[INode], f: K[Id] => T, alist: AList[K]) extends INode[T] { + protected def dependsOn = alist.toList(in) + protected def evaluate0(): Unit = setValue(f(alist.transform(in, getValue))) + } +} diff --git a/util/collection/src/main/scala/sbt/KList.scala b/util/collection/src/main/scala/sbt/KList.scala new file mode 100644 index 000000000..0b09ac9b1 --- /dev/null +++ b/util/collection/src/main/scala/sbt/KList.scala @@ -0,0 +1,53 @@ +package sbt + +import Types._ +import Classes.Applicative + +/** Heterogeneous list with each element having type M[T] for some type T.*/ +sealed trait KList[+M[_]] { + type Transform[N[_]] <: KList[N] + + /** Apply the natural transformation `f` to each element. */ + def transform[N[_]](f: M ~> N): Transform[N] + + /** Folds this list using a function that operates on the homogeneous type of the elements of this list. */ + def foldr[T](f: (M[_], T) => T, init: T): T = init // had trouble defining it in KNil + + /** Applies `f` to the elements of this list in the applicative functor defined by `ap`. */ + def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] + + /** Equivalent to `transform(f) . apply(x => x)`, this is the essence of the iterator at the level of natural transformations.*/ + def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] + + /** Discards the heterogeneous type information and constructs a plain List from this KList's elements. */ + def toList: List[M[_]] +} +final case class KCons[H, +T <: KList[M], +M[_]](head: M[H], tail: T) extends KList[M] { + final type Transform[N[_]] = KCons[H, tail.Transform[N], N] + + def transform[N[_]](f: M ~> N) = KCons(f(head), tail.transform(f)) + def toList: List[M[_]] = head :: tail.toList + def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] = + { + val g = (t: tail.Transform[Id]) => (h: H) => f(KCons[H, tail.Transform[Id], Id](h, t)) + ap.apply(tail.apply[N, H => Z](g), head) + } + def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] = + { + val tt: N[tail.Transform[P]] = tail.traverse[N, P](f) + val g = (t: tail.Transform[P]) => (h: P[H]) => KCons(h, t) + np.apply(np.map(g, tt), f(head)) + } + def :^:[A, N[x] >: M[x]](h: N[A]) = KCons(h, this) + override def foldr[T](f: (M[_], T) => T, init: T): T = f(head, tail.foldr(f, init)) +} +sealed abstract class KNil extends KList[Nothing] { + final type Transform[N[_]] = KNil + final def transform[N[_]](f: Nothing ~> N): Transform[N] = KNil + final def toList = Nil + final def apply[N[x], Z](f: KNil => Z)(implicit ap: Applicative[N]): N[Z] = ap.pure(f(KNil)) + final def traverse[N[_], P[_]](f: Nothing ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] = np.pure(KNil) +} +case object KNil extends KNil { + def :^:[M[_], H](h: M[H]): KCons[H, KNil, M] = KCons(h, this) +} diff --git a/util/collection/src/main/scala/sbt/PMap.scala b/util/collection/src/main/scala/sbt/PMap.scala new file mode 100644 index 000000000..cf0454fd9 --- /dev/null +++ b/util/collection/src/main/scala/sbt/PMap.scala @@ -0,0 +1,108 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +import collection.mutable + +trait RMap[K[_], V[_]] { + def apply[T](k: K[T]): V[T] + def get[T](k: K[T]): Option[V[T]] + def contains[T](k: K[T]): Boolean + def toSeq: Seq[(K[_], V[_])] + def toTypedSeq: Seq[TPair[_]] = toSeq.map { case (k: K[t], v) => TPair[t](k, v.asInstanceOf[V[t]]) } + def keys: Iterable[K[_]] + def values: Iterable[V[_]] + def isEmpty: Boolean + + final case class TPair[T](key: K[T], value: V[T]) +} + +trait IMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { + def put[T](k: K[T], v: V[T]): IMap[K, V] + def remove[T](k: K[T]): IMap[K, V] + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): IMap[K, V] + def mapValues[V2[_]](f: V ~> V2): IMap[K, V2] + def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l): (IMap[K, VL], IMap[K, VR]) +} +trait PMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { + def update[T](k: K[T], v: V[T]): Unit + def remove[T](k: K[T]): Option[V[T]] + def getOrUpdate[T](k: K[T], make: => V[T]): V[T] + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] +} +object PMap { + implicit def toFunction[K[_], V[_]](map: PMap[K, V]): K[_] => V[_] = k => map(k) + def empty[K[_], V[_]]: PMap[K, V] = new DelegatingPMap[K, V](new mutable.HashMap) +} +object IMap { + /** + * Only suitable for K that is invariant in its type parameter. + * Option and List keys are not suitable, for example, + * because None <:< Option[String] and None <: Option[Int]. + */ + def empty[K[_], V[_]]: IMap[K, V] = new IMap0[K, V](Map.empty) + + private[this] class IMap0[K[_], V[_]](backing: Map[K[_], V[_]]) extends AbstractRMap[K, V] with IMap[K, V] { + def get[T](k: K[T]): Option[V[T]] = (backing get k).asInstanceOf[Option[V[T]]] + def put[T](k: K[T], v: V[T]) = new IMap0[K, V](backing.updated(k, v)) + def remove[T](k: K[T]) = new IMap0[K, V](backing - k) + + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]) = + put(k, f(this get k getOrElse init)) + + def mapValues[V2[_]](f: V ~> V2) = + new IMap0[K, V2](backing.mapValues(x => f(x))) + + def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l) = + { + val mapped = backing.iterator.map { + case (k, v) => f(v) match { + case Left(l) => Left((k, l)) + case Right(r) => Right((k, r)) + } + } + val (l, r) = Util.separateE[(K[_], VL[_]), (K[_], VR[_])](mapped.toList) + (new IMap0[K, VL](l.toMap), new IMap0[K, VR](r.toMap)) + } + + def toSeq = backing.toSeq + def keys = backing.keys + def values = backing.values + def isEmpty = backing.isEmpty + + override def toString = backing.toString + } +} + +abstract class AbstractRMap[K[_], V[_]] extends RMap[K, V] { + def apply[T](k: K[T]): V[T] = get(k).get + def contains[T](k: K[T]): Boolean = get(k).isDefined +} + +/** + * Only suitable for K that is invariant in its type parameter. + * Option and List keys are not suitable, for example, + * because None <:< Option[String] and None <: Option[Int]. + */ +class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]]) extends AbstractRMap[K, V] with PMap[K, V] { + def get[T](k: K[T]): Option[V[T]] = cast[T](backing.get(k)) + def update[T](k: K[T], v: V[T]) { backing(k) = v } + def remove[T](k: K[T]) = cast(backing.remove(k)) + def getOrUpdate[T](k: K[T], make: => V[T]) = cast[T](backing.getOrElseUpdate(k, make)) + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] = + { + val v = f(this get k getOrElse init) + update(k, v) + v + } + def toSeq = backing.toSeq + def keys = backing.keys + def values = backing.values + def isEmpty = backing.isEmpty + + private[this] def cast[T](v: V[_]): V[T] = v.asInstanceOf[V[T]] + private[this] def cast[T](o: Option[V[_]]): Option[V[T]] = o map cast[T] + + override def toString = backing.toString +} diff --git a/util/collection/src/main/scala/sbt/Param.scala b/util/collection/src/main/scala/sbt/Param.scala new file mode 100644 index 000000000..19d12798a --- /dev/null +++ b/util/collection/src/main/scala/sbt/Param.scala @@ -0,0 +1,30 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +import Types._ + +// Used to emulate ~> literals +trait Param[A[_], B[_]] { + type T + def in: A[T] + def ret(out: B[T]) + def ret: B[T] +} + +object Param { + implicit def pToT[A[_], B[_]](p: Param[A, B] => Unit): A ~> B = new (A ~> B) { + def apply[s](a: A[s]): B[s] = { + val v: Param[A, B] { type T = s } = new Param[A, B] { + type T = s + def in = a + private var r: B[T] = _ + def ret(b: B[T]): Unit = { r = b } + def ret: B[T] = r + } + p(v) + v.ret + } + } +} diff --git a/util/collection/src/main/scala/sbt/Positions.scala b/util/collection/src/main/scala/sbt/Positions.scala new file mode 100755 index 000000000..5d7e1915d --- /dev/null +++ b/util/collection/src/main/scala/sbt/Positions.scala @@ -0,0 +1,20 @@ +package sbt + +sealed trait SourcePosition + +sealed trait FilePosition extends SourcePosition { + def path: String + def startLine: Int +} + +case object NoPosition extends SourcePosition + +final case class LinePosition(path: String, startLine: Int) extends FilePosition + +final case class LineRange(start: Int, end: Int) { + def shift(n: Int) = new LineRange(start + n, end + n) +} + +final case class RangePosition(path: String, range: LineRange) extends FilePosition { + def startLine = range.start +} diff --git a/util/collection/src/main/scala/sbt/Settings.scala b/util/collection/src/main/scala/sbt/Settings.scala new file mode 100644 index 000000000..eb4227d09 --- /dev/null +++ b/util/collection/src/main/scala/sbt/Settings.scala @@ -0,0 +1,642 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt + +import Types._ + +sealed trait Settings[Scope] { + def data: Map[Scope, AttributeMap] + def keys(scope: Scope): Set[AttributeKey[_]] + def scopes: Set[Scope] + def definingScope(scope: Scope, key: AttributeKey[_]): Option[Scope] + def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] + def get[T](scope: Scope, key: AttributeKey[T]): Option[T] + def getDirect[T](scope: Scope, key: AttributeKey[T]): Option[T] + def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] +} + +private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope] { + def scopes: Set[Scope] = data.keySet + def keys(scope: Scope) = data(scope).keys.toSet + def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] = data.flatMap { case (scope, map) => map.keys.map(k => f(scope, k)) } toSeq + + def get[T](scope: Scope, key: AttributeKey[T]): Option[T] = + delegates(scope).toStream.flatMap(sc => getDirect(sc, key)).headOption + def definingScope(scope: Scope, key: AttributeKey[_]): Option[Scope] = + delegates(scope).toStream.find(sc => getDirect(sc, key).isDefined) + + def getDirect[T](scope: Scope, key: AttributeKey[T]): Option[T] = + (data get scope).flatMap(_ get key) + + def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] = + { + val map = data getOrElse (scope, AttributeMap.empty) + val newData = data.updated(scope, map.put(key, value)) + new Settings0(newData, delegates) + } +} +// delegates should contain the input Scope as the first entry +// this trait is intended to be mixed into an object +trait Init[Scope] { + /** The Show instance used when a detailed String needs to be generated. It is typically used when no context is available.*/ + def showFullKey: Show[ScopedKey[_]] + + final case class ScopedKey[T](scope: Scope, key: AttributeKey[T]) extends KeyedInitialize[T] { + def scopedKey = this + } + + type SettingSeq[T] = Seq[Setting[T]] + type ScopedMap = IMap[ScopedKey, SettingSeq] + type CompiledMap = Map[ScopedKey[_], Compiled[_]] + type MapScoped = ScopedKey ~> ScopedKey + type ValidatedRef[T] = Either[Undefined, ScopedKey[T]] + type ValidatedInit[T] = Either[Seq[Undefined], Initialize[T]] + type ValidateRef = ScopedKey ~> ValidatedRef + type ScopeLocal = ScopedKey[_] => Seq[Setting[_]] + type MapConstant = ScopedKey ~> Option + + private[sbt] abstract class ValidateKeyRef { + def apply[T](key: ScopedKey[T], selfRefOk: Boolean): ValidatedRef[T] + } + + /** + * The result of this initialization is the composition of applied transformations. + * This can be useful when dealing with dynamic Initialize values. + */ + lazy val capturedTransformations: Initialize[Initialize ~> Initialize] = new TransformCapture(idK[Initialize]) + + def setting[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition = NoPosition): Setting[T] = new Setting[T](key, init, pos) + def valueStrict[T](value: T): Initialize[T] = pure(() => value) + def value[T](value: => T): Initialize[T] = pure(value _) + def pure[T](value: () => T): Initialize[T] = new Value(value) + def optional[T, U](i: Initialize[T])(f: Option[T] => U): Initialize[U] = new Optional(Some(i), f) + def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = setting[T](key, map(key)(f), NoPosition) + def bind[S, T](in: Initialize[S])(f: S => Initialize[T]): Initialize[T] = new Bind(f, in) + def map[S, T](in: Initialize[S])(f: S => T): Initialize[T] = new Apply[({ type l[L[x]] = L[S] })#l, T](f, in, AList.single[S]) + def app[K[L[x]], T](inputs: K[Initialize])(f: K[Id] => T)(implicit alist: AList[K]): Initialize[T] = new Apply[K, T](f, inputs, alist) + def uniform[S, T](inputs: Seq[Initialize[S]])(f: Seq[S] => T): Initialize[T] = + new Apply[({ type l[L[x]] = List[L[S]] })#l, T](f, inputs.toList, AList.seq[S]) + + /** + * The result of this initialization is the validated `key`. + * No dependency is introduced on `key`. If `selfRefOk` is true, validation will not fail if the key is referenced by a definition of `key`. + * That is, key := f(validated(key).value) is allowed only if `selfRefOk == true`. + */ + private[sbt] final def validated[T](key: ScopedKey[T], selfRefOk: Boolean): ValidationCapture[T] = new ValidationCapture(key, selfRefOk) + + @deprecated("Use the version with default arguments and default parameter.", "0.13.7") + final def derive[T](s: Setting[T], allowDynamic: Boolean, filter: Scope => Boolean, trigger: AttributeKey[_] => Boolean): Setting[T] = + derive(s, allowDynamic, filter, trigger, false) + /** + * Constructs a derived setting that will be automatically defined in every scope where one of its dependencies + * is explicitly defined and the where the scope matches `filter`. + * A setting initialized with dynamic dependencies is only allowed if `allowDynamic` is true. + * Only the static dependencies are tracked, however. Dependencies on previous values do not introduce a derived setting either. + */ + final def derive[T](s: Setting[T], allowDynamic: Boolean = false, filter: Scope => Boolean = const(true), trigger: AttributeKey[_] => Boolean = const(true), default: Boolean = false): Setting[T] = { + deriveAllowed(s, allowDynamic) foreach sys.error + val d = new DerivedSetting[T](s.key, s.init, s.pos, filter, trigger) + if (default) d.default() else d + } + def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] = s.init match { + case _: Bind[_, _] if !allowDynamic => Some("Cannot derive from dynamic dependencies.") + case _ => None + } + // id is used for equality + private[sbt] final def defaultSetting[T](s: Setting[T]): Setting[T] = s.default() + private[sbt] def defaultSettings(ss: Seq[Setting[_]]): Seq[Setting[_]] = ss.map(s => defaultSetting(s)) + private[this] final val nextID = new java.util.concurrent.atomic.AtomicLong + private[this] final def nextDefaultID(): Long = nextID.incrementAndGet() + + def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = new Settings0(Map.empty, delegates) + def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) { + def apply[T](k: ScopedKey[T]): T = getValue(s, k) + } + def getValue[T](s: Settings[Scope], k: ScopedKey[T]) = s.get(k.scope, k.key) getOrElse (throw new InvalidReference(k)) + def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k) + def mapScope(f: Scope => Scope): MapScoped = new MapScoped { + def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope)) + } + private final class InvalidReference(val key: ScopedKey[_]) extends RuntimeException("Internal settings error: invalid reference to " + showFullKey(key)) + + private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] = + { + val (defaults, others) = Util.separate[Setting[_], DefaultSetting[_], Setting[_]](ss) { case u: DefaultSetting[_] => Left(u); case s => Right(s) } + defaults.distinct ++ others + } + + def compiled(init: Seq[Setting[_]], actual: Boolean = true)(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): CompiledMap = + { + val initDefaults = applyDefaults(init) + // inject derived settings into scopes where their dependencies are directly defined + // and prepend per-scope settings + val derived = deriveAndLocal(initDefaults) + // group by Scope/Key, dropping dead initializations + val sMap: ScopedMap = grouped(derived) + // delegate references to undefined values according to 'delegates' + val dMap: ScopedMap = if (actual) delegate(sMap)(delegates, display) else sMap + // merge Seq[Setting[_]] into Compiled + compile(dMap) + } + def make(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): Settings[Scope] = + { + val cMap = compiled(init)(delegates, scopeLocal, display) + // order the initializations. cyclic references are detected here. + val ordered: Seq[Compiled[_]] = sort(cMap) + // evaluation: apply the initializations. + try { applyInits(ordered) } + catch { case rru: RuntimeUndefined => throw Uninitialized(cMap.keys.toSeq, delegates, rru.undefined, true) } + } + def sort(cMap: CompiledMap): Seq[Compiled[_]] = + Dag.topologicalSort(cMap.values)(_.dependencies.map(cMap)) + + def compile(sMap: ScopedMap): CompiledMap = + sMap.toTypedSeq.map { + case sMap.TPair(k, ss) => + val deps = ss flatMap { _.dependencies } toSet; + (k, new Compiled(k, deps, ss)) + } toMap; + + def grouped(init: Seq[Setting[_]]): ScopedMap = + ((IMap.empty: ScopedMap) /: init)((m, s) => add(m, s)) + + def add[T](m: ScopedMap, s: Setting[T]): ScopedMap = + m.mapValue[T](s.key, Nil, ss => append(ss, s)) + + def append[T](ss: Seq[Setting[T]], s: Setting[T]): Seq[Setting[T]] = + if (s.definitive) s :: Nil else ss :+ s + + def addLocal(init: Seq[Setting[_]])(implicit scopeLocal: ScopeLocal): Seq[Setting[_]] = + init.flatMap(_.dependencies flatMap scopeLocal) ++ init + + def delegate(sMap: ScopedMap)(implicit delegates: Scope => Seq[Scope], display: Show[ScopedKey[_]]): ScopedMap = + { + def refMap(ref: Setting[_], isFirst: Boolean) = new ValidateKeyRef { + def apply[T](k: ScopedKey[T], selfRefOk: Boolean) = + delegateForKey(sMap, k, delegates(k.scope), ref, selfRefOk || !isFirst) + } + type ValidatedSettings[T] = Either[Seq[Undefined], SettingSeq[T]] + val f = new (SettingSeq ~> ValidatedSettings) { + def apply[T](ks: Seq[Setting[T]]) = { + val (undefs, valid) = Util.separate(ks.zipWithIndex) { case (s, i) => s validateKeyReferenced refMap(s, i == 0) } + if (undefs.isEmpty) Right(valid) else Left(undefs.flatten) + } + } + type Undefs[_] = Seq[Undefined] + val (undefineds, result) = sMap.mapSeparate[Undefs, SettingSeq](f) + if (undefineds.isEmpty) + result + else + throw Uninitialized(sMap.keys.toSeq, delegates, undefineds.values.flatten.toList, false) + } + private[this] def delegateForKey[T](sMap: ScopedMap, k: ScopedKey[T], scopes: Seq[Scope], ref: Setting[_], selfRefOk: Boolean): Either[Undefined, ScopedKey[T]] = + { + val skeys = scopes.iterator.map(x => ScopedKey(x, k.key)) + val definedAt = skeys.find(sk => (selfRefOk || ref.key != sk) && (sMap contains sk)) + definedAt.toRight(Undefined(ref, k)) + } + + private[this] def applyInits(ordered: Seq[Compiled[_]])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = + { + val x = java.util.concurrent.Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors) + try { + val eval: EvaluateSettings[Scope] = new EvaluateSettings[Scope] { + override val init: Init.this.type = Init.this + def compiledSettings = ordered + def executor = x + } + eval.run + } finally { x.shutdown() } + } + + def showUndefined(u: Undefined, validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope])(implicit display: Show[ScopedKey[_]]): String = + { + val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey) + val derived = u.defining.isDerived + val refString = display(u.defining.key) + val sourceString = if (derived) "" else parenPosString(u.defining) + val guessedString = if (derived) "" else guessed.map(g => "\n Did you mean " + display(g) + " ?").toList.mkString + val derivedString = if (derived) ", which is a derived setting that needs this key to be defined in this scope." else "" + display(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString + } + private[this] def parenPosString(s: Setting[_]): String = + s.positionString match { case None => ""; case Some(s) => " (" + s + ")" } + + def guessIntendedScope(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], key: ScopedKey[_]): Option[ScopedKey[_]] = + { + val distances = validKeys.flatMap { validKey => refinedDistance(delegates, validKey, key).map(dist => (dist, validKey)) } + distances.sortBy(_._1).map(_._2).headOption + } + def refinedDistance(delegates: Scope => Seq[Scope], a: ScopedKey[_], b: ScopedKey[_]): Option[Int] = + if (a.key != b.key || a == b) None + else { + val dist = delegates(a.scope).indexOf(b.scope) + if (dist < 0) None else Some(dist) + } + + final class Uninitialized(val undefined: Seq[Undefined], override val toString: String) extends Exception(toString) + final class Undefined private[sbt] (val defining: Setting[_], val referencedKey: ScopedKey[_]) { + @deprecated("For compatibility only, use `defining` directly.", "0.13.1") + val definingKey = defining.key + @deprecated("For compatibility only, use `defining` directly.", "0.13.1") + val derived: Boolean = defining.isDerived + @deprecated("Use the non-deprecated Undefined factory method.", "0.13.1") + def this(definingKey: ScopedKey[_], referencedKey: ScopedKey[_], derived: Boolean) = this(fakeUndefinedSetting(definingKey, derived), referencedKey) + } + final class RuntimeUndefined(val undefined: Seq[Undefined]) extends RuntimeException("References to undefined settings at runtime.") { + override def getMessage = + super.getMessage + undefined.map { u => + "\n" + u.defining + " referenced from " + u.referencedKey + }.mkString + } + + @deprecated("Use the other overload.", "0.13.1") + def Undefined(definingKey: ScopedKey[_], referencedKey: ScopedKey[_], derived: Boolean): Undefined = + new Undefined(fakeUndefinedSetting(definingKey, derived), referencedKey) + private[this] def fakeUndefinedSetting[T](definingKey: ScopedKey[T], d: Boolean): Setting[T] = + { + val init: Initialize[T] = pure(() => sys.error("Dummy setting for compatibility only.")) + new Setting(definingKey, init, NoPosition) { override def isDerived = d } + } + + def Undefined(defining: Setting[_], referencedKey: ScopedKey[_]): Undefined = new Undefined(defining, referencedKey) + def Uninitialized(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], keys: Seq[Undefined], runtime: Boolean)(implicit display: Show[ScopedKey[_]]): Uninitialized = + { + assert(keys.nonEmpty) + val suffix = if (keys.length > 1) "s" else "" + val prefix = if (runtime) "Runtime reference" else "Reference" + val keysString = keys.map(u => showUndefined(u, validKeys, delegates)).mkString("\n\n ", "\n\n ", "") + new Uninitialized(keys, prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ") + } + final class Compiled[T](val key: ScopedKey[T], val dependencies: Iterable[ScopedKey[_]], val settings: Seq[Setting[T]]) { + override def toString = showFullKey(key) + } + final class Flattened(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]]) + + def flattenLocals(compiled: CompiledMap): Map[ScopedKey[_], Flattened] = + { + import collection.breakOut + val locals = compiled flatMap { case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil } + val ordered = Dag.topologicalSort(locals)(_.dependencies.flatMap(dep => if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil)) + def flatten(cmap: Map[ScopedKey[_], Flattened], key: ScopedKey[_], deps: Iterable[ScopedKey[_]]): Flattened = + new Flattened(key, deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil)) + + val empty = Map.empty[ScopedKey[_], Flattened] + val flattenedLocals = (empty /: ordered) { (cmap, c) => cmap.updated(c.key, flatten(cmap, c.key, c.dependencies)) } + compiled flatMap { + case (key, comp) => + if (key.key.isLocal) + Nil + else + Seq[(ScopedKey[_], Flattened)]((key, flatten(flattenedLocals, key, comp.dependencies))) + } + } + + def definedAtString(settings: Seq[Setting[_]]): String = + { + val posDefined = settings.flatMap(_.positionString.toList) + if (posDefined.nonEmpty) { + val header = if (posDefined.size == settings.size) "defined at:" else + "some of the defining occurrences:" + header + (posDefined.distinct mkString ("\n\t", "\n\t", "\n")) + } else "" + } + + /** + * Intersects two scopes, returning the more specific one if they intersect, or None otherwise. + */ + private[sbt] def intersect(s1: Scope, s2: Scope)(implicit delegates: Scope => Seq[Scope]): Option[Scope] = + if (delegates(s1).contains(s2)) Some(s1) // s1 is more specific + else if (delegates(s2).contains(s1)) Some(s2) // s2 is more specific + else None + + private[this] def deriveAndLocal(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal): Seq[Setting[_]] = + { + import collection.mutable + + final class Derived(val setting: DerivedSetting[_]) { + val dependencies = setting.dependencies.map(_.key) + def triggeredBy = dependencies.filter(setting.trigger) + val inScopes = new mutable.HashSet[Scope] + val outputs = new mutable.ListBuffer[Setting[_]] + } + final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) { + def dependencies = settings.flatMap(_.dependencies) + // This is mainly for use in the cyclic reference error message + override def toString = s"Derived settings for ${key.label}, ${definedAtString(settings.map(_.setting))}" + } + + // separate `derived` settings from normal settings (`defs`) + val (derived, rawDefs) = Util.separate[Setting[_], Derived, Setting[_]](init) { case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s) } + val defs = addLocal(rawDefs)(scopeLocal) + + // group derived settings by the key they define + val derivsByDef = new mutable.HashMap[AttributeKey[_], Deriveds] + for (s <- derived) { + val key = s.setting.key.key + derivsByDef.getOrElseUpdate(key, new Deriveds(key, new mutable.ListBuffer)).settings += s + } + + // sort derived settings so that dependencies come first + // this is necessary when verifying that a derived setting's dependencies exist + val ddeps = (d: Deriveds) => d.dependencies.flatMap(derivsByDef.get) + val sortedDerivs = Dag.topologicalSort(derivsByDef.values)(ddeps) + + // index derived settings by triggering key. This maps a key to the list of settings potentially derived from it. + val derivedBy = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived]] + for (s <- derived; d <- s.triggeredBy) + derivedBy.getOrElseUpdate(d, new mutable.ListBuffer) += s + + // Map a DerivedSetting[_] to the `Derived` struct wrapping it. Used to ultimately replace a DerivedSetting with + // the `Setting`s that were actually derived from it: `Derived.outputs` + val derivedToStruct: Map[DerivedSetting[_], Derived] = (derived map { s => s.setting -> s }).toMap + + // set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present + val defined = new mutable.HashSet[ScopedKey[_]] + def addDefs(ss: Seq[Setting[_]]): Unit = { for (s <- ss) defined += s.key } + addDefs(defs) + + // true iff the scoped key is in `defined`, taking delegation into account + def isDefined(key: AttributeKey[_], scope: Scope) = + delegates(scope).exists(s => defined.contains(ScopedKey(s, key))) + + // true iff all dependencies of derived setting `d` have a value (potentially via delegation) in `scope` + def allDepsDefined(d: Derived, scope: Scope, local: Set[AttributeKey[_]]): Boolean = + d.dependencies.forall(dep => local(dep) || isDefined(dep, scope)) + + // Returns the list of injectable derived settings and their local settings for `sk`. + // The settings are to be injected under `outputScope` = whichever scope is more specific of: + // * the dependency's (`sk`) scope + // * the DerivedSetting's scope in which it has been declared, `definingScope` + // provided that these two scopes intersect. + // A derived setting is injectable if: + // 1. it has not been previously injected into outputScope + // 2. it applies to outputScope (as determined by its `filter`) + // 3. all of its dependencies are defined for outputScope (allowing for delegation) + // This needs to handle local settings because a derived setting wouldn't be injected if it's local setting didn't exist yet. + val deriveFor = (sk: ScopedKey[_]) => { + val derivedForKey: List[Derived] = derivedBy.get(sk.key).toList.flatten + val scope = sk.scope + def localAndDerived(d: Derived): Seq[Setting[_]] = { + def definingScope = d.setting.key.scope + val outputScope = intersect(scope, definingScope) + outputScope collect { + case s if !d.inScopes.contains(s) && d.setting.filter(s) => + val local = d.dependencies.flatMap(dep => scopeLocal(ScopedKey(s, dep))) + if (allDepsDefined(d, s, local.map(_.key.key).toSet)) { + d.inScopes.add(s) + val out = local :+ d.setting.setScope(s) + d.outputs ++= out + out + } else + Nil + } getOrElse Nil + } + derivedForKey.flatMap(localAndDerived) + } + + val processed = new mutable.HashSet[ScopedKey[_]] + + // derives settings, transitively so that a derived setting can trigger another + def process(rem: List[Setting[_]]): Unit = rem match { + case s :: ss => + val sk = s.key + val ds = if (processed.add(sk)) deriveFor(sk) else Nil + addDefs(ds) + process(ds ::: ss) + case Nil => + } + process(defs.toList) + + // Take all the original defs and DerivedSettings along with locals, replace each DerivedSetting with the actual + // settings that were derived. + val allDefs = addLocal(init)(scopeLocal) + allDefs flatMap { case d: DerivedSetting[_] => (derivedToStruct get d map (_.outputs)).toStream.flatten; case s => Stream(s) } + } + + sealed trait Initialize[T] { + def dependencies: Seq[ScopedKey[_]] + def apply[S](g: T => S): Initialize[S] + + @deprecated("Will be made private.", "0.13.2") + def mapReferenced(g: MapScoped): Initialize[T] + @deprecated("Will be made private.", "0.13.2") + def mapConstant(g: MapConstant): Initialize[T] + + @deprecated("Will be made private.", "0.13.2") + def validateReferenced(g: ValidateRef): ValidatedInit[T] = + validateKeyReferenced(new ValidateKeyRef { def apply[T](key: ScopedKey[T], selfRefOk: Boolean) = g(key) }) + + private[sbt] def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] + + def evaluate(map: Settings[Scope]): T + def zip[S](o: Initialize[S]): Initialize[(T, S)] = zipTupled(o)(idFun) + def zipWith[S, U](o: Initialize[S])(f: (T, S) => U): Initialize[U] = zipTupled(o)(f.tupled) + private[this] def zipTupled[S, U](o: Initialize[S])(f: ((T, S)) => U): Initialize[U] = + new Apply[({ type l[L[x]] = (L[T], L[S]) })#l, U](f, (this, o), AList.tuple2[T, S]) + /** A fold on the static attributes of this and nested Initializes. */ + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S + } + object Initialize { + implicit def joinInitialize[T](s: Seq[Initialize[T]]): JoinInitSeq[T] = new JoinInitSeq(s) + final class JoinInitSeq[T](s: Seq[Initialize[T]]) { + def joinWith[S](f: Seq[T] => S): Initialize[S] = uniform(s)(f) + def join: Initialize[Seq[T]] = uniform(s)(idFun) + } + def join[T](inits: Seq[Initialize[T]]): Initialize[Seq[T]] = uniform(inits)(idFun) + def joinAny[M[_]](inits: Seq[Initialize[M[T]] forSome { type T }]): Initialize[Seq[M[_]]] = + join(inits.asInstanceOf[Seq[Initialize[M[Any]]]]).asInstanceOf[Initialize[Seq[M[T] forSome { type T }]]] + } + object SettingsDefinition { + implicit def unwrapSettingsDefinition(d: SettingsDefinition): Seq[Setting[_]] = d.settings + implicit def wrapSettingsDefinition(ss: Seq[Setting[_]]): SettingsDefinition = new SettingList(ss) + } + sealed trait SettingsDefinition { + def settings: Seq[Setting[_]] + } + final class SettingList(val settings: Seq[Setting[_]]) extends SettingsDefinition + sealed class Setting[T] private[Init] (val key: ScopedKey[T], val init: Initialize[T], val pos: SourcePosition) extends SettingsDefinition { + def settings = this :: Nil + def definitive: Boolean = !init.dependencies.contains(key) + def dependencies: Seq[ScopedKey[_]] = remove(init.dependencies, key) + @deprecated("Will be made private.", "0.13.2") + def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos) + @deprecated("Will be made private.", "0.13.2") + def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] = (init validateReferenced g).right.map(newI => make(key, newI, pos)) + + private[sbt] def validateKeyReferenced(g: ValidateKeyRef): Either[Seq[Undefined], Setting[T]] = + (init validateKeyReferenced g).right.map(newI => make(key, newI, pos)) + + def mapKey(g: MapScoped): Setting[T] = make(g(key), init, pos) + def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = make(key, init(t => f(key, t)), pos) + @deprecated("Will be made private.", "0.13.2") + def mapConstant(g: MapConstant): Setting[T] = make(key, init mapConstant g, pos) + def withPos(pos: SourcePosition) = make(key, init, pos) + def positionString: Option[String] = pos match { + case pos: FilePosition => Some(pos.path + ":" + pos.startLine) + case NoPosition => None + } + private[sbt] def mapInitialize(f: Initialize[T] => Initialize[T]): Setting[T] = make(key, f(init), pos) + override def toString = "setting(" + key + ") at " + pos + + protected[this] def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = new Setting[T](key, init, pos) + protected[sbt] def isDerived: Boolean = false + private[sbt] def setScope(s: Scope): Setting[T] = make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos) + /** Turn this setting into a `DefaultSetting` if it's not already, otherwise returns `this` */ + private[sbt] def default(id: => Long = nextDefaultID()): DefaultSetting[T] = DefaultSetting(key, init, pos, id) + } + private[Init] sealed class DerivedSetting[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, val filter: Scope => Boolean, val trigger: AttributeKey[_] => Boolean) extends Setting[T](sk, i, p) { + override def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = new DerivedSetting[T](key, init, pos, filter, trigger) + protected[sbt] override def isDerived: Boolean = true + override def default(_id: => Long): DefaultSetting[T] = new DerivedSetting[T](sk, i, p, filter, trigger) with DefaultSetting[T] { val id = _id } + override def toString = "derived " + super.toString + } + // Only keep the first occurrence of this setting and move it to the front so that it has lower precedence than non-defaults. + // This is intended for internal sbt use only, where alternatives like Plugin.globalSettings are not available. + private[Init] sealed trait DefaultSetting[T] extends Setting[T] { + val id: Long + override def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = super.make(key, init, pos) default id + override final def hashCode = id.hashCode + override final def equals(o: Any): Boolean = o match { case d: DefaultSetting[_] => d.id == id; case _ => false } + override def toString = s"default($id) " + super.toString + override def default(id: => Long) = this + } + + object DefaultSetting { + def apply[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, _id: Long) = new Setting[T](sk, i, p) with DefaultSetting[T] { val id = _id } + } + + private[this] def handleUndefined[T](vr: ValidatedInit[T]): Initialize[T] = vr match { + case Left(undefs) => throw new RuntimeUndefined(undefs) + case Right(x) => x + } + + private[this] lazy val getValidated = + new (ValidatedInit ~> Initialize) { def apply[T](v: ValidatedInit[T]) = handleUndefined[T](v) } + + // mainly for reducing generated class count + private[this] def validateKeyReferencedT(g: ValidateKeyRef) = + new (Initialize ~> ValidatedInit) { def apply[T](i: Initialize[T]) = i validateKeyReferenced g } + + private[this] def mapReferencedT(g: MapScoped) = + new (Initialize ~> Initialize) { def apply[T](i: Initialize[T]) = i mapReferenced g } + + private[this] def mapConstantT(g: MapConstant) = + new (Initialize ~> Initialize) { def apply[T](i: Initialize[T]) = i mapConstant g } + + private[this] def evaluateT(g: Settings[Scope]) = + new (Initialize ~> Id) { def apply[T](i: Initialize[T]) = i evaluate g } + + private[this] def deps(ls: Seq[Initialize[_]]): Seq[ScopedKey[_]] = ls.flatMap(_.dependencies) + + sealed trait Keyed[S, T] extends Initialize[T] { + def scopedKey: ScopedKey[S] + def transform: S => T + final def dependencies = scopedKey :: Nil + final def apply[Z](g: T => Z): Initialize[Z] = new GetValue(scopedKey, g compose transform) + final def evaluate(ss: Settings[Scope]): T = transform(getValue(ss, scopedKey)) + final def mapReferenced(g: MapScoped): Initialize[T] = new GetValue(g(scopedKey), transform) + private[sbt] final def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] = g(scopedKey, false) match { + case Left(un) => Left(un :: Nil) + case Right(nk) => Right(new GetValue(nk, transform)) + } + final def mapConstant(g: MapConstant): Initialize[T] = g(scopedKey) match { + case None => this + case Some(const) => new Value(() => transform(const)) + } + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init + } + private[this] final class GetValue[S, T](val scopedKey: ScopedKey[S], val transform: S => T) extends Keyed[S, T] + trait KeyedInitialize[T] extends Keyed[T, T] { + final val transform = idFun[T] + } + + private[sbt] final class TransformCapture(val f: Initialize ~> Initialize) extends Initialize[Initialize ~> Initialize] { + def dependencies = Nil + def apply[Z](g2: (Initialize ~> Initialize) => Z): Initialize[Z] = map(this)(g2) + def evaluate(ss: Settings[Scope]): Initialize ~> Initialize = f + def mapReferenced(g: MapScoped) = new TransformCapture(mapReferencedT(g) ∙ f) + def mapConstant(g: MapConstant) = new TransformCapture(mapConstantT(g) ∙ f) + def validateKeyReferenced(g: ValidateKeyRef) = Right(new TransformCapture(getValidated ∙ validateKeyReferencedT(g) ∙ f)) + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init + } + private[sbt] final class ValidationCapture[T](val key: ScopedKey[T], val selfRefOk: Boolean) extends Initialize[ScopedKey[T]] { + def dependencies = Nil + def apply[Z](g2: ScopedKey[T] => Z): Initialize[Z] = map(this)(g2) + def evaluate(ss: Settings[Scope]) = key + def mapReferenced(g: MapScoped) = new ValidationCapture(g(key), selfRefOk) + def mapConstant(g: MapConstant) = this + def validateKeyReferenced(g: ValidateKeyRef) = g(key, selfRefOk) match { + case Left(un) => Left(un :: Nil) + case Right(k) => Right(new ValidationCapture(k, selfRefOk)) + } + + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init + } + private[sbt] final class Bind[S, T](val f: S => Initialize[T], val in: Initialize[S]) extends Initialize[T] { + def dependencies = in.dependencies + def apply[Z](g: T => Z): Initialize[Z] = new Bind[S, Z](s => f(s)(g), in) + def evaluate(ss: Settings[Scope]): T = f(in evaluate ss) evaluate ss + def mapReferenced(g: MapScoped) = new Bind[S, T](s => f(s) mapReferenced g, in mapReferenced g) + def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).right.map { validIn => + new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn) + } + def mapConstant(g: MapConstant) = new Bind[S, T](s => f(s) mapConstant g, in mapConstant g) + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = in.processAttributes(init)(f) + } + private[sbt] final class Optional[S, T](val a: Option[Initialize[S]], val f: Option[S] => T) extends Initialize[T] { + def dependencies = deps(a.toList) + def apply[Z](g: T => Z): Initialize[Z] = new Optional[S, Z](a, g compose f) + def mapReferenced(g: MapScoped) = new Optional(a map mapReferencedT(g).fn, f) + def validateKeyReferenced(g: ValidateKeyRef) = a match { + case None => Right(this) + case Some(i) => Right(new Optional(i.validateKeyReferenced(g).right.toOption, f)) + } + def mapConstant(g: MapConstant): Initialize[T] = new Optional(a map mapConstantT(g).fn, f) + def evaluate(ss: Settings[Scope]): T = f(a.flatMap(i => trapBadRef(evaluateT(ss)(i)))) + // proper solution is for evaluate to be deprecated or for external use only and a new internal method returning Either be used + private[this] def trapBadRef[A](run: => A): Option[A] = try Some(run) catch { case e: InvalidReference => None } + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = a match { + case None => init + case Some(i) => i.processAttributes(init)(f) + } + } + private[sbt] final class Value[T](val value: () => T) extends Initialize[T] { + def dependencies = Nil + def mapReferenced(g: MapScoped) = this + def validateKeyReferenced(g: ValidateKeyRef) = Right(this) + def apply[S](g: T => S) = new Value[S](() => g(value())) + def mapConstant(g: MapConstant) = this + def evaluate(map: Settings[Scope]): T = value() + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init + } + private[sbt] final object StaticScopes extends Initialize[Set[Scope]] { + def dependencies = Nil + def mapReferenced(g: MapScoped) = this + def validateKeyReferenced(g: ValidateKeyRef) = Right(this) + def apply[S](g: Set[Scope] => S) = map(this)(g) + def mapConstant(g: MapConstant) = this + def evaluate(map: Settings[Scope]) = map.scopes + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init + } + private[sbt] final class Apply[K[L[x]], T](val f: K[Id] => T, val inputs: K[Initialize], val alist: AList[K]) extends Initialize[T] { + def dependencies = deps(alist.toList(inputs)) + def mapReferenced(g: MapScoped) = mapInputs(mapReferencedT(g)) + def apply[S](g: T => S) = new Apply(g compose f, inputs, alist) + def mapConstant(g: MapConstant) = mapInputs(mapConstantT(g)) + def mapInputs(g: Initialize ~> Initialize): Initialize[T] = new Apply(f, alist.transform(inputs, g), alist) + def evaluate(ss: Settings[Scope]) = f(alist.transform(inputs, evaluateT(ss))) + def validateKeyReferenced(g: ValidateKeyRef) = + { + val tx = alist.transform(inputs, validateKeyReferencedT(g)) + val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten) + val get = new (ValidatedInit ~> Initialize) { def apply[T](vr: ValidatedInit[T]) = vr.right.get } + if (undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs) + } + + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = + (init /: alist.toList(inputs)) { (v, i) => i.processAttributes(v)(f) } + } + private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v) +} diff --git a/util/collection/src/main/scala/sbt/Show.scala b/util/collection/src/main/scala/sbt/Show.scala new file mode 100644 index 000000000..1f8e9703b --- /dev/null +++ b/util/collection/src/main/scala/sbt/Show.scala @@ -0,0 +1,8 @@ +package sbt + +trait Show[T] { + def apply(t: T): String +} +object Show { + def apply[T](f: T => String): Show[T] = new Show[T] { def apply(t: T): String = f(t) } +} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/ShowLines.scala b/util/collection/src/main/scala/sbt/ShowLines.scala new file mode 100644 index 000000000..126b6360e --- /dev/null +++ b/util/collection/src/main/scala/sbt/ShowLines.scala @@ -0,0 +1,15 @@ +package sbt + +trait ShowLines[A] { + def showLines(a: A): Seq[String] +} +object ShowLines { + def apply[A](f: A => Seq[String]): ShowLines[A] = + new ShowLines[A] { + def showLines(a: A): Seq[String] = f(a) + } + + implicit class ShowLinesOp[A: ShowLines](a: A) { + def lines: Seq[String] = implicitly[ShowLines[A]].showLines(a) + } +} diff --git a/util/collection/src/main/scala/sbt/Signal.scala b/util/collection/src/main/scala/sbt/Signal.scala new file mode 100644 index 000000000..e8c9e7e6c --- /dev/null +++ b/util/collection/src/main/scala/sbt/Signal.scala @@ -0,0 +1,85 @@ +package sbt + +object Signals { + val CONT = "CONT" + val INT = "INT" + def withHandler[T](handler: () => Unit, signal: String = INT)(action: () => T): T = + { + val result = + try { + val signals = new Signals0 + signals.withHandler(signal, handler, action) + } catch { case e: LinkageError => Right(action()) } + + result match { + case Left(e) => throw e + case Right(v) => v + } + } + + /** Helper interface so we can expose internals of signal-isms to others. */ + sealed trait Registration { + def remove(): Unit + } + /** + * Register a signal handler that can be removed later. + * NOTE: Does not stack with other signal handlers!!!! + */ + def register(handler: () => Unit, signal: String = INT): Registration = + // TODO - Maybe we can just ignore things if not is-supported. + if (supported(signal)) { + import sun.misc.{ Signal, SignalHandler } + val intSignal = new Signal(signal) + val newHandler = new SignalHandler { + def handle(sig: Signal) { handler() } + } + val oldHandler = Signal.handle(intSignal, newHandler) + object unregisterNewHandler extends Registration { + override def remove(): Unit = { + Signal.handle(intSignal, oldHandler) + } + } + unregisterNewHandler + } else { + // TODO - Maybe we should just throw an exception if we don't support signals... + object NullUnregisterNewHandler extends Registration { + override def remove(): Unit = () + } + NullUnregisterNewHandler + } + + def supported(signal: String): Boolean = + try { + val signals = new Signals0 + signals.supported(signal) + } catch { case e: LinkageError => false } +} + +// Must only be referenced using a +// try { } catch { case e: LinkageError => ... } +// block to +private final class Signals0 { + def supported(signal: String): Boolean = + { + import sun.misc.Signal + try { new Signal(signal); true } + catch { case e: IllegalArgumentException => false } + } + + // returns a LinkageError in `action` as Left(t) in order to avoid it being + // incorrectly swallowed as missing Signal/SignalHandler + def withHandler[T](signal: String, handler: () => Unit, action: () => T): Either[Throwable, T] = + { + import sun.misc.{ Signal, SignalHandler } + val intSignal = new Signal(signal) + val newHandler = new SignalHandler { + def handle(sig: Signal) { handler() } + } + + val oldHandler = Signal.handle(intSignal, newHandler) + + try Right(action()) + catch { case e: LinkageError => Left(e) } + finally Signal.handle(intSignal, oldHandler) + } +} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/TypeFunctions.scala b/util/collection/src/main/scala/sbt/TypeFunctions.scala new file mode 100644 index 000000000..74f0a7d99 --- /dev/null +++ b/util/collection/src/main/scala/sbt/TypeFunctions.scala @@ -0,0 +1,50 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +trait TypeFunctions { + type Id[X] = X + sealed trait Const[A] { type Apply[B] = A } + sealed trait ConstK[A] { type l[L[x]] = A } + sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] } + sealed trait ∙[A[_], B[_]] { type l[T] = A[B[T]] } + sealed trait P1of2[M[_, _], A] { type Apply[B] = M[A, B]; type Flip[B] = M[B, A] } + + final val left = new (Id ~> P1of2[Left, Nothing]#Flip) { def apply[T](t: T) = Left(t) } + final val right = new (Id ~> P1of2[Right, Nothing]#Apply) { def apply[T](t: T) = Right(t) } + final val some = new (Id ~> Some) { def apply[T](t: T) = Some(t) } + final def idFun[T] = (t: T) => t + final def const[A, B](b: B): A => B = _ => b + final def idK[M[_]]: M ~> M = new (M ~> M) { def apply[T](m: M[T]): M[T] = m } + + def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l = + f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call. castless version: + /* new ( (M ∙ G)#l ~> (N ∙ G)#l ) { + def apply[T](mg: M[G[T]]): N[G[T]] = f(mg) + }*/ + + implicit def toFn1[A, B](f: A => B): Fn1[A, B] = new Fn1[A, B] { + def ∙[C](g: C => A) = f compose g + } + + type Endo[T] = T => T + type ~>|[A[_], B[_]] = A ~> Compose[Option, B]#Apply +} +object TypeFunctions extends TypeFunctions + +trait ~>[-A[_], +B[_]] { outer => + def apply[T](a: A[T]): B[T] + // directly on ~> because of type inference limitations + final def ∙[C[_]](g: C ~> A): C ~> B = new (C ~> B) { def apply[T](c: C[T]) = outer.apply(g(c)) } + final def ∙[C, D](g: C => D)(implicit ev: D <:< A[D]): C => B[D] = i => apply(ev(g(i))) + final def fn[T] = (t: A[T]) => apply[T](t) +} +object ~> { + import TypeFunctions._ + val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a } + implicit def tcIdEquals: (Id ~> Id) = Id +} +trait Fn1[A, B] { + def ∙[C](g: C => A): C => B +} \ No newline at end of file diff --git a/util/collection/src/main/scala/sbt/Types.scala b/util/collection/src/main/scala/sbt/Types.scala new file mode 100644 index 000000000..29994f3d1 --- /dev/null +++ b/util/collection/src/main/scala/sbt/Types.scala @@ -0,0 +1,12 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +object Types extends Types + +trait Types extends TypeFunctions { + val :^: = KCons + type :+:[H, T <: HList] = HCons[H, T] + val :+: = HCons +} diff --git a/util/collection/src/main/scala/sbt/Util.scala b/util/collection/src/main/scala/sbt/Util.scala new file mode 100644 index 000000000..befc7b5a9 --- /dev/null +++ b/util/collection/src/main/scala/sbt/Util.scala @@ -0,0 +1,43 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt + +import java.util.Locale + +object Util { + def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value) + + def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) = + separate(ps)(Types.idFun) + + def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = + { + val (a, b) = ((Nil: Seq[A], Nil: Seq[B]) /: ps)((xs, y) => prependEither(xs, f(y))) + (a.reverse, b.reverse) + } + + def prependEither[A, B](acc: (Seq[A], Seq[B]), next: Either[A, B]): (Seq[A], Seq[B]) = + next match { + case Left(l) => (l +: acc._1, acc._2) + case Right(r) => (acc._1, r +: acc._2) + } + + def pairID[A, B] = (a: A, b: B) => (a, b) + + private[this] lazy val Hypen = """-(\p{javaLowerCase})""".r + def hasHyphen(s: String): Boolean = s.indexOf('-') >= 0 + @deprecated("Use the properly spelled version: hyphenToCamel", "0.13.0") + def hypenToCamel(s: String): String = hyphenToCamel(s) + def hyphenToCamel(s: String): String = + if (hasHyphen(s)) + Hypen.replaceAllIn(s, _.group(1).toUpperCase(Locale.ENGLISH)) + else + s + + private[this] lazy val Camel = """(\p{javaLowerCase})(\p{javaUpperCase})""".r + def camelToHypen(s: String): String = + Camel.replaceAllIn(s, m => m.group(1) + "-" + m.group(2).toLowerCase(Locale.ENGLISH)) + + def quoteIfKeyword(s: String): String = if (ScalaKeywords.values(s)) '`' + s + '`' else s +} diff --git a/util/collection/src/test/scala/DagSpecification.scala b/util/collection/src/test/scala/DagSpecification.scala new file mode 100644 index 000000000..abf9ddf28 --- /dev/null +++ b/util/collection/src/test/scala/DagSpecification.scala @@ -0,0 +1,50 @@ +/* sbt -- Simple Build Tool + * Copyright 2008 Mark Harrah */ + +package sbt + +import org.scalacheck._ +import Prop._ + +import scala.collection.mutable.HashSet + +object DagSpecification extends Properties("Dag") { + property("No repeated nodes") = forAll { (dag: TestDag) => isSet(dag.topologicalSort) } + property("Sort contains node") = forAll { (dag: TestDag) => dag.topologicalSort.contains(dag) } + property("Dependencies precede node") = forAll { (dag: TestDag) => dependenciesPrecedeNodes(dag.topologicalSort) } + + implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen)) + private def dagGen(nodeCount: Int): Gen[TestDag] = + { + val nodes = new HashSet[TestDag] + def nonterminalGen(p: Gen.Parameters): Gen[TestDag] = + { + for (i <- 0 until nodeCount; nextDeps <- Gen.someOf(nodes).apply(p)) + nodes += new TestDag(i, nextDeps) + for (nextDeps <- Gen.someOf(nodes)) yield new TestDag(nodeCount, nextDeps) + } + Gen.parameterized(nonterminalGen) + } + + private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size + private def dependenciesPrecedeNodes(sort: List[TestDag]) = + { + val seen = new HashSet[TestDag] + def iterate(remaining: List[TestDag]): Boolean = + { + remaining match { + case Nil => true + case node :: tail => + if (node.dependencies.forall(seen.contains) && !seen.contains(node)) { + seen += node + iterate(tail) + } else + false + } + } + iterate(sort) + } +} +class TestDag(id: Int, val dependencies: Iterable[TestDag]) extends Dag[TestDag] { + override def toString = id + "->" + dependencies.mkString("[", ",", "]") +} \ No newline at end of file diff --git a/util/collection/src/test/scala/KeyTest.scala b/util/collection/src/test/scala/KeyTest.scala new file mode 100644 index 000000000..f48e3742a --- /dev/null +++ b/util/collection/src/test/scala/KeyTest.scala @@ -0,0 +1,32 @@ +package sbt + +import org.scalacheck._ +import Prop._ + +object KeyTest extends Properties("AttributeKey") { + property("equality") = { + compare(AttributeKey[Int]("test"), AttributeKey[Int]("test"), true) && + compare(AttributeKey[Int]("test"), AttributeKey[Int]("test", "description"), true) && + compare(AttributeKey[Int]("test", "a"), AttributeKey[Int]("test", "b"), true) && + compare(AttributeKey[Int]("test"), AttributeKey[Int]("tests"), false) && + compare(AttributeKey[Int]("test"), AttributeKey[Double]("test"), false) && + compare(AttributeKey[java.lang.Integer]("test"), AttributeKey[Int]("test"), false) && + compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, String]]("test"), true) && + compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, _]]("test"), false) + } + + def compare(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) = + ("a.label: " + a.label) |: + ("a.manifest: " + a.manifest) |: + ("b.label: " + b.label) |: + ("b.manifest: " + b.manifest) |: + ("expected equal? " + same) |: + compare0(a, b, same) + + def compare0(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) = + if (same) { + ("equality" |: (a == b)) && + ("hash" |: (a.hashCode == b.hashCode)) + } else + ("equality" |: (a != b)) +} \ No newline at end of file diff --git a/util/collection/src/test/scala/LiteralTest.scala b/util/collection/src/test/scala/LiteralTest.scala new file mode 100644 index 000000000..35ef373ca --- /dev/null +++ b/util/collection/src/test/scala/LiteralTest.scala @@ -0,0 +1,17 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +import Types._ + +// compilation test +object LiteralTest { + def x[A[_], B[_]](f: A ~> B) = f + + import Param._ + val f = x { (p: Param[Option, List]) => p.ret(p.in.toList) } + + val a: List[Int] = f(Some(3)) + val b: List[String] = f(Some("aa")) +} \ No newline at end of file diff --git a/util/collection/src/test/scala/PMapTest.scala b/util/collection/src/test/scala/PMapTest.scala new file mode 100644 index 000000000..6a6c558c1 --- /dev/null +++ b/util/collection/src/test/scala/PMapTest.scala @@ -0,0 +1,18 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt + +import Types._ + +// compilation test +object PMapTest { + val mp = new DelegatingPMap[Some, Id](new collection.mutable.HashMap) + mp(Some("asdf")) = "a" + mp(Some(3)) = 9 + val x = Some(3) :^: Some("asdf") :^: KNil + val y = x.transform[Id](mp) + assert(y.head == 9) + assert(y.tail.head == "a") + assert(y.tail.tail == KNil) +} \ No newline at end of file diff --git a/util/collection/src/test/scala/SettingsExample.scala b/util/collection/src/test/scala/SettingsExample.scala new file mode 100644 index 000000000..b48bb27fc --- /dev/null +++ b/util/collection/src/test/scala/SettingsExample.scala @@ -0,0 +1,87 @@ +package sbt + +/** Define our settings system */ + +// A basic scope indexed by an integer. +final case class Scope(nestIndex: Int, idAtIndex: Int = 0) + +// Extend the Init trait. +// (It is done this way because the Scope type parameter is used everywhere in Init. +// Lots of type constructors would become binary, which as you may know requires lots of type lambdas +// when you want a type function with only one parameter. +// That would be a general pain.) +object SettingsExample extends Init[Scope] { + // Provides a way of showing a Scope+AttributeKey[_] + val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] { + def apply(key: ScopedKey[_]) = s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}" + } + + // A sample delegation function that delegates to a Scope with a lower index. + val delegates: Scope => Seq[Scope] = { + case s @ Scope(index, proj) => + s +: (if (index <= 0) Nil else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index - 1)) }) + } + + // Not using this feature in this example. + val scopeLocal: ScopeLocal = _ => Nil + + // These three functions + a scope (here, Scope) are sufficient for defining our settings system. +} + +/** Usage Example **/ + +object SettingsUsage { + import SettingsExample._ + import Types._ + + // Define some keys + val a = AttributeKey[Int]("a") + val b = AttributeKey[Int]("b") + + // Scope these keys + val a3 = ScopedKey(Scope(3), a) + val a4 = ScopedKey(Scope(4), a) + val a5 = ScopedKey(Scope(5), a) + + val b4 = ScopedKey(Scope(4), b) + + // Define some settings + val mySettings: Seq[Setting[_]] = Seq( + setting(a3, value(3)), + setting(b4, map(a4)(_ * 3)), + update(a5)(_ + 1) + ) + + // "compiles" and applies the settings. + // This can be split into multiple steps to access intermediate results if desired. + // The 'inspect' command operates on the output of 'compile', for example. + val applied: Settings[Scope] = make(mySettings)(delegates, scopeLocal, showFullKey) + + // Show results. + /* for(i <- 0 to 5; k <- Seq(a, b)) { + println( k.label + i + " = " + applied.get( Scope(i), k) ) + }*/ + + /** + * Output: + * For the None results, we never defined the value and there was no value to delegate to. + * For a3, we explicitly defined it to be 3. + * a4 wasn't defined, so it delegates to a3 according to our delegates function. + * b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies by 3 + * a5 is defined as the previous value of a5 + 1 and + * since no previous value of a5 was defined, it delegates to a4, resulting in 3+1=4. + * b5 isn't defined explicitly, so it delegates to b4 and is therefore equal to 9 as well + * a0 = None + * b0 = None + * a1 = None + * b1 = None + * a2 = None + * b2 = None + * a3 = Some(3) + * b3 = None + * a4 = Some(3) + * b4 = Some(9) + * a5 = Some(4) + * b5 = Some(9) + */ +} diff --git a/util/collection/src/test/scala/SettingsTest.scala b/util/collection/src/test/scala/SettingsTest.scala new file mode 100644 index 000000000..d97b1056a --- /dev/null +++ b/util/collection/src/test/scala/SettingsTest.scala @@ -0,0 +1,195 @@ +package sbt + +import org.scalacheck._ +import Prop._ +import SettingsUsage._ +import SettingsExample._ + +object SettingsTest extends Properties("settings") { + + import scala.reflect.Manifest + + final val ChainMax = 5000 + lazy val chainLengthGen = Gen.choose(1, ChainMax) + + property("Basic settings test") = secure(all(tests: _*)) + + property("Basic chain") = forAll(chainLengthGen) { (i: Int) => + val abs = math.abs(i) + singleIntTest(chain(abs, value(0)), abs) + } + property("Basic bind chain") = forAll(chainLengthGen) { (i: Int) => + val abs = math.abs(i) + singleIntTest(chainBind(value(abs)), 0) + } + + property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference } + final def allowedReference(intermediate: Int): Prop = + { + val top = value(intermediate) + def iterate(init: Initialize[Int]): Initialize[Int] = + bind(init) { t => + if (t <= 0) + top + else + iterate(value(t - 1)) + } + evaluate(setting(chk, iterate(top)) :: Nil); true + } + + property("Derived setting chain depending on (prev derived, normal setting)") = forAllNoShrink(Gen.choose(1, 100).label("numSettings")) { derivedSettings } + final def derivedSettings(nr: Int): Prop = + { + val genScopedKeys = { + // We wan + // t to generate lists of keys that DO NOT inclue the "ch" key we use to check things. + val attrKeys = mkAttrKeys[Int](nr).filter(_.forall(_.label != "ch")) + attrKeys map (_ map (ak => ScopedKey(Scope(0), ak))) + }.label("scopedKeys").filter(_.nonEmpty) + forAll(genScopedKeys) { scopedKeys => + try { + // Note; It's evil to grab last IF you haven't verified the set can't be empty. + val last = scopedKeys.last + val derivedSettings: Seq[Setting[Int]] = ( + for { + List(scoped0, scoped1) <- chk :: scopedKeys sliding 2 + nextInit = if (scoped0 == chk) chk + else (scoped0 zipWith chk) { (p, _) => p + 1 } + } yield derive(setting(scoped1, nextInit)) + ).toSeq + + { + // Note: This causes a cycle refernec error, quite frequently. + checkKey(last, Some(nr - 1), evaluate(setting(chk, value(0)) +: derivedSettings)) :| "Not derived?" + } && { + checkKey(last, None, evaluate(derivedSettings)) :| "Should not be derived" + } + } catch { + case t: Throwable => + // TODO - For debugging only. + t.printStackTrace(System.err) + throw t + } + } + } + + private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] = + { + import Gen._ + val nonEmptyAlphaStr = + nonEmptyListOf(alphaChar).map(_.mkString).suchThat(_.forall(_.isLetter)) + + (for { + list <- Gen.listOfN(nr, nonEmptyAlphaStr) suchThat (l => l.size == l.distinct.size) + item <- list + } yield AttributeKey[T](item)).label(s"mkAttrKeys($nr)") + } + + property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = derivedKeepsPosition + final def derivedKeepsPosition: Prop = + { + val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a")) + val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b")) + val prop1 = { + val settings: Seq[Setting[_]] = Seq( + setting(a, value(3)), + setting(b, value(6)), + derive(setting(b, a)), + setting(a, value(5)), + setting(b, value(8)) + ) + val ev = evaluate(settings) + checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev) + } + val prop2 = { + val settings: Seq[Setting[Int]] = Seq( + setting(a, value(3)), + setting(b, value(6)), + derive(setting(b, a)), + setting(a, value(5)) + ) + val ev = evaluate(settings) + checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev) + } + prop1 && prop2 + } + + property("DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope } + final def derivedSettingsScope(nrProjects: Int): Prop = + { + forAll(mkAttrKeys[Int](2)) { + case List(key, derivedKey) => + val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key) + val projectDerivedKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), derivedKey) + val globalKey = ScopedKey(Scope(0), key) + val globalDerivedKey = ScopedKey(Scope(0), derivedKey) + // Each project defines an initial value, but the update is defined in globalKey. + // However, the derived Settings that come from this should be scoped in each project. + val settings: Seq[Setting[_]] = + derive(setting(globalDerivedKey, SettingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0))) + val ev = evaluate(settings) + // Also check that the key has no value at the "global" scope + val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev) + checkKey(globalDerivedKey, None, ev) && Prop.all(props: _*) + } + } + + // Circular (dynamic) references currently loop infinitely. + // This is the expected behavior (detecting dynamic cycles is expensive), + // but it may be necessary to provide an option to detect them (with a performance hit) + // This would test that cycle detection. + // property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ } + final def checkCircularReferences(intermediate: Int): Prop = + { + val ccr = new CCR(intermediate) + try { evaluate(setting(chk, ccr.top) :: Nil); false } + catch { case e: java.lang.Exception => true } + } + + def tests = + for (i <- 0 to 5; k <- Seq(a, b)) yield { + val expected = expectedValues(2 * i + (if (k == a) 0 else 1)) + checkKey[Int](ScopedKey(Scope(i), k), expected, applied) + } + + lazy val expectedValues = None :: None :: None :: None :: None :: None :: Some(3) :: None :: Some(3) :: Some(9) :: Some(4) :: Some(9) :: Nil + + lazy val ch = AttributeKey[Int]("ch") + lazy val chk = ScopedKey(Scope(0), ch) + def chain(i: Int, prev: Initialize[Int]): Initialize[Int] = + if (i <= 0) prev else chain(i - 1, prev(_ + 1)) + + def chainBind(prev: Initialize[Int]): Initialize[Int] = + bind(prev) { v => + if (v <= 0) prev else chainBind(value(v - 1)) + } + def singleIntTest(i: Initialize[Int], expected: Int) = + { + val eval = evaluate(setting(chk, i) :: Nil) + checkKey(chk, Some(expected), eval) + } + + def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) = + { + val value = settings.get(key.scope, key.key) + ("Key: " + key) |: + ("Value: " + value) |: + ("Expected: " + expected) |: + (value == expected) + } + + def evaluate(settings: Seq[Setting[_]]): Settings[Scope] = + try { make(settings)(delegates, scopeLocal, showFullKey) } + catch { case e: Throwable => e.printStackTrace; throw e } +} +// This setup is a workaround for module synchronization issues +final class CCR(intermediate: Int) { + lazy val top = iterate(value(intermediate), intermediate) + def iterate(init: Initialize[Int], i: Int): Initialize[Int] = + bind(init) { t => + if (t <= 0) + top + else + iterate(value(t - 1), t - 1) + } +} diff --git a/util/complete/NOTICE b/util/complete/NOTICE new file mode 100644 index 000000000..a6f2c1de4 --- /dev/null +++ b/util/complete/NOTICE @@ -0,0 +1,3 @@ +Simple Build Tool: Completion Component +Copyright 2010 Mark Harrah +Licensed under BSD-style license (see LICENSE) \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/LineReader.scala b/util/complete/src/main/scala/sbt/LineReader.scala new file mode 100644 index 000000000..b85190f92 --- /dev/null +++ b/util/complete/src/main/scala/sbt/LineReader.scala @@ -0,0 +1,139 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009 Mark Harrah + */ +package sbt + +import jline.console.ConsoleReader +import jline.console.history.{ FileHistory, MemoryHistory } +import java.io.{ File, InputStream, PrintWriter } +import complete.Parser +import java.util.concurrent.atomic.AtomicBoolean + +abstract class JLine extends LineReader { + protected[this] val handleCONT: Boolean + protected[this] val reader: ConsoleReader + + def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) } + + private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]) = + readLineWithHistory(prompt, mask) match { + case null => None + case x => Some(x.trim) + } + + private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): String = + reader.getHistory match { + case fh: FileHistory => + try { readLineDirect(prompt, mask) } + finally { fh.flush() } + case _ => readLineDirect(prompt, mask) + } + + private[this] def readLineDirect(prompt: String, mask: Option[Char]): String = + if (handleCONT) + Signals.withHandler(() => resume(), signal = Signals.CONT)(() => readLineDirectRaw(prompt, mask)) + else + readLineDirectRaw(prompt, mask) + private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): String = + { + val newprompt = handleMultilinePrompt(prompt) + mask match { + case Some(m) => reader.readLine(newprompt, m) + case None => reader.readLine(newprompt) + } + } + + private[this] def handleMultilinePrompt(prompt: String): String = { + val lines = """\r?\n""".r.split(prompt) + lines.length match { + case 0 | 1 => prompt + case _ => reader.print(lines.init.mkString("\n") + "\n"); lines.last; + } + } + + private[this] def resume() { + jline.TerminalFactory.reset + JLine.terminal.init + reader.drawLine() + reader.flush() + } +} +private object JLine { + private[this] val TerminalProperty = "jline.terminal" + + fixTerminalProperty() + + // translate explicit class names to type in order to support + // older Scala, since it shaded classes but not the system property + private[sbt] def fixTerminalProperty() { + val newValue = System.getProperty(TerminalProperty) match { + case "jline.UnixTerminal" => "unix" + case null if System.getProperty("sbt.cygwin") != null => "unix" + case "jline.WindowsTerminal" => "windows" + case "jline.AnsiWindowsTerminal" => "windows" + case "jline.UnsupportedTerminal" => "none" + case x => x + } + if (newValue != null) System.setProperty(TerminalProperty, newValue) + } + + // When calling this, ensure that enableEcho has been or will be called. + // TerminalFactory.get will initialize the terminal to disable echo. + private def terminal = jline.TerminalFactory.get + private def withTerminal[T](f: jline.Terminal => T): T = + synchronized { + val t = terminal + t.synchronized { f(t) } + } + /** + * For accessing the JLine Terminal object. + * This ensures synchronized access as well as re-enabling echo after getting the Terminal. + */ + def usingTerminal[T](f: jline.Terminal => T): T = + withTerminal { t => + t.restore + f(t) + } + def createReader(): ConsoleReader = createReader(None) + def createReader(historyPath: Option[File]): ConsoleReader = + usingTerminal { t => + val cr = new ConsoleReader + cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650 + cr.setBellEnabled(false) + val h = historyPath match { + case None => new MemoryHistory + case Some(file) => new FileHistory(file) + } + h.setMaxSize(MaxHistorySize) + cr.setHistory(h) + cr + } + def withJLine[T](action: => T): T = + withTerminal { t => + t.init + try { action } + finally { t.restore } + } + + def simple(historyPath: Option[File], handleCONT: Boolean = HandleCONT): SimpleReader = new SimpleReader(historyPath, handleCONT) + val MaxHistorySize = 500 + val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT) +} + +trait LineReader { + def readLine(prompt: String, mask: Option[Char] = None): Option[String] +} +final class FullReader(historyPath: Option[File], complete: Parser[_], val handleCONT: Boolean = JLine.HandleCONT) extends JLine { + protected[this] val reader = + { + val cr = JLine.createReader(historyPath) + sbt.complete.JLineCompletion.installCustomCompletor(cr, complete) + cr + } +} + +class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean) extends JLine { + protected[this] val reader = JLine.createReader(historyPath) +} +object SimpleReader extends SimpleReader(None, JLine.HandleCONT) + diff --git a/util/complete/src/main/scala/sbt/complete/Completions.scala b/util/complete/src/main/scala/sbt/complete/Completions.scala new file mode 100644 index 000000000..5237ad26d --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/Completions.scala @@ -0,0 +1,144 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt.complete + +/** + * Represents a set of completions. + * It exists instead of implicitly defined operations on top of Set[Completion] + * for laziness. + */ +sealed trait Completions { + def get: Set[Completion] + final def x(o: Completions): Completions = flatMap(_ x o) + final def ++(o: Completions): Completions = Completions(get ++ o.get) + final def +:(o: Completion): Completions = Completions(get + o) + final def filter(f: Completion => Boolean): Completions = Completions(get filter f) + final def filterS(f: String => Boolean): Completions = filter(c => f(c.append)) + override def toString = get.mkString("Completions(", ",", ")") + final def flatMap(f: Completion => Completions): Completions = Completions(get.flatMap(c => f(c).get)) + final def map(f: Completion => Completion): Completions = Completions(get map f) + override final def hashCode = get.hashCode + override final def equals(o: Any) = o match { case c: Completions => get == c.get; case _ => false } +} +object Completions { + /** Returns a lazy Completions instance using the provided Completion Set. */ + def apply(cs: => Set[Completion]): Completions = new Completions { + lazy val get = cs + } + + /** Returns a strict Completions instance using the provided Completion Set. */ + def strict(cs: Set[Completion]): Completions = apply(cs) + + /** + * No suggested completions, not even the empty Completion. + * This typically represents invalid input. + */ + val nil: Completions = strict(Set.empty) + + /** + * Only includes an empty Suggestion. + * This typically represents valid input that either has no completions or accepts no further input. + */ + val empty: Completions = strict(Set.empty + Completion.empty) + + /** Returns a strict Completions instance containing only the provided Completion.*/ + def single(c: Completion): Completions = strict(Set.empty + c) +} + +/** + * Represents a completion. + * The abstract members `display` and `append` are best explained with an example. + * + * Assuming space-delimited tokens, processing this: + * am is are w + * could produce these Completions: + * Completion { display = "was"; append = "as" } + * Completion { display = "were"; append = "ere" } + * to suggest the tokens "was" and "were". + * + * In this way, two pieces of information are preserved: + * 1) what needs to be appended to the current input if a completion is selected + * 2) the full token being completed, which is useful for presenting a user with choices to select + */ +sealed trait Completion { + /** The proposed suffix to append to the existing input to complete the last token in the input.*/ + def append: String + /** The string to present to the user to represent the full token being suggested.*/ + def display: String + /** True if this Completion is suggesting the empty string.*/ + def isEmpty: Boolean + + /** Appends the completions in `o` with the completions in this Completion.*/ + def ++(o: Completion): Completion = Completion.concat(this, o) + final def x(o: Completions): Completions = if (Completion evaluatesRight this) o.map(this ++ _) else Completions.strict(Set.empty + this) + override final lazy val hashCode = Completion.hashCode(this) + override final def equals(o: Any) = o match { case c: Completion => Completion.equal(this, c); case _ => false } +} +final class DisplayOnly(val display: String) extends Completion { + def isEmpty = display.isEmpty + def append = "" + override def toString = "{" + display + "}" +} +final class Token(val display: String, val append: String) extends Completion { + @deprecated("Retained only for compatibility. All information is now in `display` and `append`.", "0.12.1") + lazy val prepend = display.stripSuffix(append) + def isEmpty = display.isEmpty && append.isEmpty + override final def toString = "[" + display + "]++" + append +} +final class Suggestion(val append: String) extends Completion { + def isEmpty = append.isEmpty + def display = append + override def toString = append +} +object Completion { + def concat(a: Completion, b: Completion): Completion = + (a, b) match { + case (as: Suggestion, bs: Suggestion) => suggestion(as.append + bs.append) + case (at: Token, _) if at.append.isEmpty => b + case _ if a.isEmpty => b + case _ => a + } + def evaluatesRight(a: Completion): Boolean = + a match { + case _: Suggestion => true + case at: Token if at.append.isEmpty => true + case _ => a.isEmpty + } + + def equal(a: Completion, b: Completion): Boolean = + (a, b) match { + case (as: Suggestion, bs: Suggestion) => as.append == bs.append + case (ad: DisplayOnly, bd: DisplayOnly) => ad.display == bd.display + case (at: Token, bt: Token) => at.display == bt.display && at.append == bt.append + case _ => false + } + + def hashCode(a: Completion): Int = + a match { + case as: Suggestion => (0, as.append).hashCode + case ad: DisplayOnly => (1, ad.display).hashCode + case at: Token => (2, at.display, at.append).hashCode + } + + val empty: Completion = suggestion("") + def single(c: Char): Completion = suggestion(c.toString) + + // TODO: make strict in 0.13.0 to match DisplayOnly + def displayOnly(value: => String): Completion = new DisplayOnly(value) + @deprecated("Use displayOnly.", "0.12.1") + def displayStrict(value: String): Completion = displayOnly(value) + + // TODO: make strict in 0.13.0 to match Token + def token(prepend: => String, append: => String): Completion = new Token(prepend + append, append) + @deprecated("Use token.", "0.12.1") + def tokenStrict(prepend: String, append: String): Completion = token(prepend, append) + + /** @since 0.12.1 */ + def tokenDisplay(append: String, display: String): Completion = new Token(display, append) + + // TODO: make strict in 0.13.0 to match Suggestion + def suggestion(value: => String): Completion = new Suggestion(value) + @deprecated("Use suggestion.", "0.12.1") + def suggestStrict(value: String): Completion = suggestion(value) +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/EditDistance.scala b/util/complete/src/main/scala/sbt/complete/EditDistance.scala new file mode 100644 index 000000000..95ed0c91f --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/EditDistance.scala @@ -0,0 +1,41 @@ +package sbt.complete + +import java.lang.Character.{ toLowerCase => lower } + +/** @author Paul Phillips*/ +object EditDistance { + /** + * Translated from the java version at + * http://www.merriampark.com/ld.htm + * which is declared to be public domain. + */ + def levenshtein(s: String, t: String, insertCost: Int = 1, deleteCost: Int = 1, subCost: Int = 1, transposeCost: Int = 1, matchCost: Int = 0, caseCost: Int = 1, transpositions: Boolean = false): Int = { + val n = s.length + val m = t.length + if (n == 0) return m + if (m == 0) return n + + val d = Array.ofDim[Int](n + 1, m + 1) + 0 to n foreach (x => d(x)(0) = x) + 0 to m foreach (x => d(0)(x) = x) + + for (i <- 1 to n; s_i = s(i - 1); j <- 1 to m) { + val t_j = t(j - 1) + val cost = if (s_i == t_j) matchCost else if (lower(s_i) == lower(t_j)) caseCost else subCost + val tcost = if (s_i == t_j) matchCost else transposeCost + + val c1 = d(i - 1)(j) + deleteCost + val c2 = d(i)(j - 1) + insertCost + val c3 = d(i - 1)(j - 1) + cost + + d(i)(j) = c1 min c2 min c3 + + if (transpositions) { + if (i > 1 && j > 1 && s(i - 1) == t(j - 2) && s(i - 2) == t(j - 1)) + d(i)(j) = d(i)(j) min (d(i - 2)(j - 2) + cost) + } + } + + d(n)(m) + } +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/ExampleSource.scala b/util/complete/src/main/scala/sbt/complete/ExampleSource.scala new file mode 100644 index 000000000..52d96246b --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/ExampleSource.scala @@ -0,0 +1,59 @@ +package sbt.complete + +import java.io.File +import sbt.IO + +/** + * These sources of examples are used in parsers for user input completion. An example of such a source is the + * [[sbt.complete.FileExamples]] class, which provides a list of suggested files to the user as they press the + * TAB key in the console. + */ +trait ExampleSource { + /** + * @return a (possibly lazy) list of completion example strings. These strings are continuations of user's input. The + * user's input is incremented with calls to [[withAddedPrefix]]. + */ + def apply(): Iterable[String] + + /** + * @param addedPrefix a string that just typed in by the user. + * @return a new source of only those examples that start with the string typed by the user so far (with addition of + * the just added prefix). + */ + def withAddedPrefix(addedPrefix: String): ExampleSource +} + +/** + * A convenience example source that wraps any collection of strings into a source of examples. + * @param examples the examples that will be displayed to the user when they press the TAB key. + */ +sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource { + override def withAddedPrefix(addedPrefix: String): ExampleSource = FixedSetExamples(examplesWithRemovedPrefix(addedPrefix)) + + override def apply(): Iterable[String] = examples + + private def examplesWithRemovedPrefix(prefix: String) = examples.collect { + case example if example startsWith prefix => example substring prefix.length + } +} + +/** + * Provides path completion examples based on files in the base directory. + * @param base the directory within which this class will search for completion examples. + * @param prefix the part of the path already written by the user. + */ +class FileExamples(base: File, prefix: String = "") extends ExampleSource { + override def apply(): Stream[String] = files(base).map(_ substring prefix.length) + + override def withAddedPrefix(addedPrefix: String): FileExamples = new FileExamples(base, prefix + addedPrefix) + + protected def files(directory: File): Stream[String] = { + val childPaths = IO.listFiles(directory).toStream + val prefixedDirectChildPaths = childPaths map { IO.relativize(base, _).get } filter { _ startsWith prefix } + val dirsToRecurseInto = childPaths filter { _.isDirectory } map { IO.relativize(base, _).get } filter { dirStartsWithPrefix } + prefixedDirectChildPaths append dirsToRecurseInto.flatMap(dir => files(new File(base, dir))) + } + + private def dirStartsWithPrefix(relativizedPath: String): Boolean = + (relativizedPath startsWith prefix) || (prefix startsWith relativizedPath) +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/History.scala b/util/complete/src/main/scala/sbt/complete/History.scala new file mode 100644 index 000000000..26d0a27c6 --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/History.scala @@ -0,0 +1,45 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt +package complete + +import History.number +import java.io.File + +final class History private (val lines: IndexedSeq[String], val path: Option[File], error: String => Unit) extends NotNull { + private def reversed = lines.reverse + + def all: Seq[String] = lines + def size = lines.length + def !! : Option[String] = !-(1) + def apply(i: Int): Option[String] = if (0 <= i && i < size) Some(lines(i)) else { sys.error("Invalid history index: " + i); None } + def !(i: Int): Option[String] = apply(i) + + def !(s: String): Option[String] = + number(s) match { + case Some(n) => if (n < 0) !-(-n) else apply(n) + case None => nonEmpty(s) { reversed.find(_.startsWith(s)) } + } + def !-(n: Int): Option[String] = apply(size - n - 1) + + def !?(s: String): Option[String] = nonEmpty(s) { reversed.drop(1).find(_.contains(s)) } + + private def nonEmpty[T](s: String)(act: => Option[T]): Option[T] = + if (s.isEmpty) { + sys.error("No action specified to history command") + None + } else + act + + def list(historySize: Int, show: Int): Seq[String] = + lines.toList.drop((lines.size - historySize) max 0).zipWithIndex.map { case (line, number) => " " + number + " " + line }.takeRight(show max 1) +} + +object History { + def apply(lines: Seq[String], path: Option[File], error: String => Unit): History = new History(lines.toIndexedSeq, path, sys.error) + + def number(s: String): Option[Int] = + try { Some(s.toInt) } + catch { case e: NumberFormatException => None } +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/HistoryCommands.scala b/util/complete/src/main/scala/sbt/complete/HistoryCommands.scala new file mode 100644 index 000000000..1e124c583 --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/HistoryCommands.scala @@ -0,0 +1,73 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt +package complete + +import java.io.File + +object HistoryCommands { + val Start = "!" + // second characters + val Contains = "?" + val Last = "!" + val ListCommands = ":" + + def ContainsFull = h(Contains) + def LastFull = h(Last) + def ListFull = h(ListCommands) + + def ListN = ListFull + "n" + def ContainsString = ContainsFull + "string" + def StartsWithString = Start + "string" + def Previous = Start + "-n" + def Nth = Start + "n" + + private def h(s: String) = Start + s + def plainCommands = Seq(ListFull, Start, LastFull, ContainsFull) + + def descriptions = Seq( + LastFull -> "Execute the last command again", + ListFull -> "Show all previous commands", + ListN -> "Show the last n commands", + Nth -> ("Execute the command with index n, as shown by the " + ListFull + " command"), + Previous -> "Execute the nth command before this one", + StartsWithString -> "Execute the most recent command starting with 'string'", + ContainsString -> "Execute the most recent command containing 'string'" + ) + def helpString = "History commands:\n " + (descriptions.map { case (c, d) => c + " " + d }).mkString("\n ") + def printHelp(): Unit = + println(helpString) + def printHistory(history: complete.History, historySize: Int, show: Int): Unit = + history.list(historySize, show).foreach(println) + + import DefaultParsers._ + + val MaxLines = 500 + lazy val num = token(NatBasic, "") + lazy val last = Last ^^^ { execute(_ !!) } + lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => + (h: History) => { printHistory(h, MaxLines, show); Some(Nil) } + } + lazy val execStr = flag('?') ~ token(any.+.string, "") map { + case (contains, str) => + execute(h => if (contains) h !? str else h ! str) + } + lazy val execInt = flag('-') ~ num map { + case (neg, value) => + execute(h => if (neg) h !- value else h ! value) + } + lazy val help = success((h: History) => { printHelp(); Some(Nil) }) + + def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => + { + val command = f(h).filterNot(_.startsWith(Start)) + val lines = h.lines.toArray + command.foreach(lines(lines.length - 1) = _) + h.path foreach { h => IO.writeLines(h, lines) } + Some(command.toList) + } + + val actionParser: Parser[complete.History => Option[List[String]]] = + Start ~> (help | last | execInt | list | execStr) // execStr must come last +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala b/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala new file mode 100644 index 000000000..fed89541f --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala @@ -0,0 +1,156 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt.complete + +import jline.console.ConsoleReader +import jline.console.completer.{ CandidateListCompletionHandler, Completer, CompletionHandler } +import scala.annotation.tailrec +import collection.JavaConversions + +object JLineCompletion { + def installCustomCompletor(reader: ConsoleReader, parser: Parser[_]): Unit = + installCustomCompletor(reader)(parserAsCompletor(parser)) + def installCustomCompletor(reader: ConsoleReader)(complete: (String, Int) => (Seq[String], Seq[String])): Unit = + installCustomCompletor(customCompletor(complete), reader) + def installCustomCompletor(complete: (ConsoleReader, Int) => Boolean, reader: ConsoleReader): Unit = + { + reader.removeCompleter(DummyCompletor) + reader.addCompleter(DummyCompletor) + reader.setCompletionHandler(new CustomHandler(complete)) + } + + private[this] final class CustomHandler(completeImpl: (ConsoleReader, Int) => Boolean) extends CompletionHandler { + private[this] var previous: Option[(String, Int)] = None + private[this] var level: Int = 1 + override def complete(reader: ConsoleReader, candidates: java.util.List[CharSequence], position: Int) = { + val current = Some(bufferSnapshot(reader)) + level = if (current == previous) level + 1 else 1 + previous = current + try completeImpl(reader, level) + catch { + case e: Exception => + reader.print("\nException occurred while determining completions.") + e.printStackTrace() + false + } + } + } + + // always provides dummy completions so that the custom completion handler gets called + // (ConsoleReader doesn't call the handler if there aren't any completions) + // the custom handler will then throw away the candidates and call the custom function + private[this] final object DummyCompletor extends Completer { + override def complete(buffer: String, cursor: Int, candidates: java.util.List[CharSequence]): Int = + { + candidates.asInstanceOf[java.util.List[String]] add "dummy" + 0 + } + } + + def parserAsCompletor(p: Parser[_]): (String, Int) => (Seq[String], Seq[String]) = + (str, level) => convertCompletions(Parser.completions(p, str, level)) + + def convertCompletions(c: Completions): (Seq[String], Seq[String]) = + { + val cs = c.get + if (cs.isEmpty) + (Nil, "{invalid input}" :: Nil) + else + convertCompletions(cs) + } + def convertCompletions(cs: Set[Completion]): (Seq[String], Seq[String]) = + { + val (insert, display) = + ((Set.empty[String], Set.empty[String]) /: cs) { + case (t @ (insert, display), comp) => + if (comp.isEmpty) t else (insert + comp.append, appendNonEmpty(display, comp.display)) + } + (insert.toSeq, display.toSeq.sorted) + } + def appendNonEmpty(set: Set[String], add: String) = if (add.trim.isEmpty) set else set + add + + def customCompletor(f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean = + (reader, level) => { + val success = complete(beforeCursor(reader), reader => f(reader, level), reader) + reader.flush() + success + } + + def bufferSnapshot(reader: ConsoleReader): (String, Int) = + { + val b = reader.getCursorBuffer + (b.buffer.toString, b.cursor) + } + def beforeCursor(reader: ConsoleReader): String = + { + val b = reader.getCursorBuffer + b.buffer.substring(0, b.cursor) + } + + // returns false if there was nothing to insert and nothing to display + def complete(beforeCursor: String, completions: String => (Seq[String], Seq[String]), reader: ConsoleReader): Boolean = + { + val (insert, display) = completions(beforeCursor) + val common = commonPrefix(insert) + if (common.isEmpty) + if (display.isEmpty) + () + else + showCompletions(display, reader) + else + appendCompletion(common, reader) + + !(common.isEmpty && display.isEmpty) + } + + def appendCompletion(common: String, reader: ConsoleReader): Unit = { + reader.getCursorBuffer.write(common) + reader.redrawLine() + } + + /** + * `display` is assumed to be the exact strings requested to be displayed. + * In particular, duplicates should have been removed already. + */ + def showCompletions(display: Seq[String], reader: ConsoleReader): Unit = { + printCompletions(display, reader) + reader.drawLine() + } + def printCompletions(cs: Seq[String], reader: ConsoleReader): Unit = { + val print = shouldPrint(cs, reader) + reader.println() + if (print) printLinesAndColumns(cs, reader) + } + def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader): Unit = { + val (lines, columns) = cs partition hasNewline + for (line <- lines) { + reader.print(line) + if (line.charAt(line.length - 1) != '\n') + reader.println() + } + reader.printColumns(JavaConversions.seqAsJavaList(columns.map(_.trim))) + } + def hasNewline(s: String): Boolean = s.indexOf('\n') >= 0 + def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean = + { + val size = cs.size + (size <= reader.getAutoprintThreshold) || + confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader) + } + def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean = + { + reader.println() + reader.print(prompt) + reader.flush() + reader.readCharacter(trueC, falseC) == trueC + } + + def commonPrefix(s: Seq[String]): String = if (s.isEmpty) "" else s reduceLeft commonPrefix + def commonPrefix(a: String, b: String): String = + { + val len = a.length min b.length + @tailrec def loop(i: Int): Int = if (i >= len) len else if (a(i) != b(i)) i else loop(i + 1) + a.substring(0, loop(0)) + } +} diff --git a/util/complete/src/main/scala/sbt/complete/Parser.scala b/util/complete/src/main/scala/sbt/complete/Parser.scala new file mode 100644 index 000000000..892119f6c --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/Parser.scala @@ -0,0 +1,848 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2010, 2011 Mark Harrah + */ +package sbt.complete + +import Parser._ +import sbt.Types.{ left, right, some } +import sbt.Util.{ makeList, separate } + +/** + * A String parser that provides semi-automatic tab completion. + * A successful parse results in a value of type `T`. + * The methods in this trait are what must be implemented to define a new Parser implementation, but are not typically useful for common usage. + * Instead, most useful methods for combining smaller parsers into larger parsers are implicitly added by the [[RichParser]] type. + */ +sealed trait Parser[+T] { + def derive(i: Char): Parser[T] + def resultEmpty: Result[T] + def result: Option[T] + def completions(level: Int): Completions + def failure: Option[Failure] + def isTokenStart = false + def ifValid[S](p: => Parser[S]): Parser[S] + def valid: Boolean +} +sealed trait RichParser[A] { + /** Apply the original Parser and then apply `next` (in order). The result of both is provides as a pair. */ + def ~[B](next: Parser[B]): Parser[(A, B)] + + /** Apply the original Parser one or more times and provide the non-empty sequence of results.*/ + def + : Parser[Seq[A]] + + /** Apply the original Parser zero or more times and provide the (potentially empty) sequence of results.*/ + def * : Parser[Seq[A]] + + /** Apply the original Parser zero or one times, returning None if it was applied zero times or the result wrapped in Some if it was applied once.*/ + def ? : Parser[Option[A]] + + /** Apply either the original Parser or `b`.*/ + def |[B >: A](b: Parser[B]): Parser[B] + + /** Apply either the original Parser or `b`.*/ + def ||[B](b: Parser[B]): Parser[Either[A, B]] + + /** Apply the original Parser to the input and then apply `f` to the result.*/ + def map[B](f: A => B): Parser[B] + + /** + * Returns the original parser. This is useful for converting literals to Parsers. + * For example, `'c'.id` or `"asdf".id` + */ + def id: Parser[A] + + /** Apply the original Parser, but provide `value` as the result if it succeeds. */ + def ^^^[B](value: B): Parser[B] + + /** Apply the original Parser, but provide `alt` as the result if it fails.*/ + def ??[B >: A](alt: B): Parser[B] + + /** + * Produces a Parser that applies the original Parser and then applies `next` (in order), discarding the result of `next`. + * (The arrow point in the direction of the retained result.) + */ + def <~[B](b: Parser[B]): Parser[A] + + /** + * Produces a Parser that applies the original Parser and then applies `next` (in order), discarding the result of the original parser. + * (The arrow point in the direction of the retained result.) + */ + def ~>[B](b: Parser[B]): Parser[B] + + /** Uses the specified message if the original Parser fails.*/ + def !!!(msg: String): Parser[A] + + /** + * If an exception is thrown by the original Parser, + * capture it and fail locally instead of allowing the exception to propagate up and terminate parsing. + */ + def failOnException: Parser[A] + + @deprecated("Use `not` and explicitly provide the failure message", "0.12.2") + def unary_- : Parser[Unit] + + /** + * Apply the original parser, but only succeed if `o` also succeeds. + * Note that `o` does not need to consume the same amount of input to satisfy this condition. + */ + def &(o: Parser[_]): Parser[A] + + @deprecated("Use `and` and `not` and explicitly provide the failure message", "0.12.2") + def -(o: Parser[_]): Parser[A] + + /** Explicitly defines the completions for the original Parser.*/ + def examples(s: String*): Parser[A] + + /** Explicitly defines the completions for the original Parser.*/ + def examples(s: Set[String], check: Boolean = false): Parser[A] + + /** + * @param exampleSource the source of examples when displaying completions to the user. + * @param maxNumberOfExamples limits the number of examples that the source of examples should return. This can + * prevent lengthy pauses and avoids bad interactive user experience. + * @param removeInvalidExamples indicates whether completion examples should be checked for validity (against the + * given parser). Invalid examples will be filtered out and only valid suggestions will + * be displayed. + * @return a new parser with a new source of completions. + */ + def examples(exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] + + /** + * @param exampleSource the source of examples when displaying completions to the user. + * @return a new parser with a new source of completions. It displays at most 25 completion examples and does not + * remove invalid examples. + */ + def examples(exampleSource: ExampleSource): Parser[A] = examples(exampleSource, maxNumberOfExamples = 25, removeInvalidExamples = false) + + /** Converts a Parser returning a Char sequence to a Parser returning a String.*/ + def string(implicit ev: A <:< Seq[Char]): Parser[String] + + /** + * Produces a Parser that filters the original parser. + * If 'f' is not true when applied to the output of the original parser, the Parser returned by this method fails. + * The failure message is constructed by applying `msg` to the String that was successfully parsed by the original parser. + */ + def filter(f: A => Boolean, msg: String => String): Parser[A] + + /** Applies the original parser, applies `f` to the result to get the next parser, and applies that parser and uses its result for the overall result. */ + def flatMap[B](f: A => Parser[B]): Parser[B] +} + +/** Contains Parser implementation helper methods not typically needed for using parsers. */ +object Parser extends ParserMain { + sealed abstract class Result[+T] { + def isFailure: Boolean + def isValid: Boolean + def errors: Seq[String] + def or[B >: T](b: => Result[B]): Result[B] + def either[B](b: => Result[B]): Result[Either[T, B]] + def map[B](f: T => B): Result[B] + def flatMap[B](f: T => Result[B]): Result[B] + def &&(b: => Result[_]): Result[T] + def filter(f: T => Boolean, msg: => String): Result[T] + def seq[B](b: => Result[B]): Result[(T, B)] = app(b)((m, n) => (m, n)) + def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] + def toEither: Either[() => Seq[String], T] + } + final case class Value[+T](value: T) extends Result[T] { + def isFailure = false + def isValid: Boolean = true + def errors = Nil + def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] = b match { + case fail: Failure => fail + case Value(bv) => Value(f(value, bv)) + } + def &&(b: => Result[_]): Result[T] = b match { case f: Failure => f; case _ => this } + def or[B >: T](b: => Result[B]): Result[B] = this + def either[B](b: => Result[B]): Result[Either[T, B]] = Value(Left(value)) + def map[B](f: T => B): Result[B] = Value(f(value)) + def flatMap[B](f: T => Result[B]): Result[B] = f(value) + def filter(f: T => Boolean, msg: => String): Result[T] = if (f(value)) this else mkFailure(msg) + def toEither = Right(value) + } + final class Failure private[sbt] (mkErrors: => Seq[String], val definitive: Boolean) extends Result[Nothing] { + lazy val errors: Seq[String] = mkErrors + def isFailure = true + def isValid = false + def map[B](f: Nothing => B) = this + def flatMap[B](f: Nothing => Result[B]) = this + def or[B](b: => Result[B]): Result[B] = b match { + case v: Value[B] => v + case f: Failure => if (definitive) this else this ++ f + } + def either[B](b: => Result[B]): Result[Either[Nothing, B]] = b match { + case Value(v) => Value(Right(v)) + case f: Failure => if (definitive) this else this ++ f + } + def filter(f: Nothing => Boolean, msg: => String) = this + def app[B, C](b: => Result[B])(f: (Nothing, B) => C): Result[C] = this + def &&(b: => Result[_]) = this + def toEither = Left(() => errors) + + private[sbt] def ++(f: Failure) = mkFailures(errors ++ f.errors) + } + def mkFailures(errors: => Seq[String], definitive: Boolean = false): Failure = new Failure(errors.distinct, definitive) + def mkFailure(error: => String, definitive: Boolean = false): Failure = new Failure(error :: Nil, definitive) + + @deprecated("This method is deprecated and will be removed in the next major version. Use the parser directly to check for invalid completions.", since = "0.13.2") + def checkMatches(a: Parser[_], completions: Seq[String]): Unit = { + val bad = completions.filter(apply(a)(_).resultEmpty.isFailure) + if (bad.nonEmpty) sys.error("Invalid example completions: " + bad.mkString("'", "', '", "'")) + } + + def tuple[A, B](a: Option[A], b: Option[B]): Option[(A, B)] = + (a, b) match { case (Some(av), Some(bv)) => Some((av, bv)); case _ => None } + + def mapParser[A, B](a: Parser[A], f: A => B): Parser[B] = + a.ifValid { + a.result match { + case Some(av) => success(f(av)) + case None => new MapParser(a, f) + } + } + + def bindParser[A, B](a: Parser[A], f: A => Parser[B]): Parser[B] = + a.ifValid { + a.result match { + case Some(av) => f(av) + case None => new BindParser(a, f) + } + } + + def filterParser[T](a: Parser[T], f: T => Boolean, seen: String, msg: String => String): Parser[T] = + a.ifValid { + a.result match { + case Some(av) if f(av) => success(av) + case _ => new Filter(a, f, seen, msg) + } + } + + def seqParser[A, B](a: Parser[A], b: Parser[B]): Parser[(A, B)] = + a.ifValid { + b.ifValid { + (a.result, b.result) match { + case (Some(av), Some(bv)) => success((av, bv)) + case (Some(av), None) => b map { bv => (av, bv) } + case (None, Some(bv)) => a map { av => (av, bv) } + case (None, None) => new SeqParser(a, b) + } + } + } + + def choiceParser[A, B](a: Parser[A], b: Parser[B]): Parser[Either[A, B]] = + if (a.valid) + if (b.valid) new HetParser(a, b) else a.map(left.fn) + else + b.map(right.fn) + + def opt[T](a: Parser[T]): Parser[Option[T]] = + if (a.valid) new Optional(a) else success(None) + + def onFailure[T](delegate: Parser[T], msg: String): Parser[T] = + if (delegate.valid) new OnFailure(delegate, msg) else failure(msg) + def trapAndFail[T](delegate: Parser[T]): Parser[T] = + delegate.ifValid(new TrapAndFail(delegate)) + + def zeroOrMore[T](p: Parser[T]): Parser[Seq[T]] = repeat(p, 0, Infinite) + def oneOrMore[T](p: Parser[T]): Parser[Seq[T]] = repeat(p, 1, Infinite) + + def repeat[T](p: Parser[T], min: Int = 0, max: UpperBound = Infinite): Parser[Seq[T]] = + repeat(None, p, min, max, Nil) + private[complete] def repeat[T](partial: Option[Parser[T]], repeated: Parser[T], min: Int, max: UpperBound, revAcc: List[T]): Parser[Seq[T]] = + { + assume(min >= 0, "Minimum must be greater than or equal to zero (was " + min + ")") + assume(max >= min, "Minimum must be less than or equal to maximum (min: " + min + ", max: " + max + ")") + + def checkRepeated(invalidButOptional: => Parser[Seq[T]]): Parser[Seq[T]] = + repeated match { + case i: Invalid if min == 0 => invalidButOptional + case i: Invalid => i + case _ => + repeated.result match { + case Some(value) => success(revAcc reverse_::: value :: Nil) // revAcc should be Nil here + case None => if (max.isZero) success(revAcc.reverse) else new Repeat(partial, repeated, min, max, revAcc) + } + } + + partial match { + case Some(part) => + part.ifValid { + part.result match { + case Some(value) => repeat(None, repeated, min, max, value :: revAcc) + case None => checkRepeated(part.map(lv => (lv :: revAcc).reverse)) + } + } + case None => checkRepeated(success(Nil)) + } + } + + @deprecated("Explicitly call `and` and `not` to provide the failure message.", "0.12.2") + def sub[T](a: Parser[T], b: Parser[_]): Parser[T] = and(a, not(b)) + + def and[T](a: Parser[T], b: Parser[_]): Parser[T] = a.ifValid(b.ifValid(new And(a, b))) +} +trait ParserMain { + /** Provides combinators for Parsers.*/ + implicit def richParser[A](a: Parser[A]): RichParser[A] = new RichParser[A] { + def ~[B](b: Parser[B]) = seqParser(a, b) + def ||[B](b: Parser[B]) = choiceParser(a, b) + def |[B >: A](b: Parser[B]) = homParser(a, b) + def ? = opt(a) + def * = zeroOrMore(a) + def + = oneOrMore(a) + def map[B](f: A => B) = mapParser(a, f) + def id = a + + def ^^^[B](value: B): Parser[B] = a map { _ => value } + def ??[B >: A](alt: B): Parser[B] = a.? map { _ getOrElse alt } + def <~[B](b: Parser[B]): Parser[A] = (a ~ b) map { case av ~ _ => av } + def ~>[B](b: Parser[B]): Parser[B] = (a ~ b) map { case _ ~ bv => bv } + def !!!(msg: String): Parser[A] = onFailure(a, msg) + def failOnException: Parser[A] = trapAndFail(a) + + def unary_- = not(a) + def &(o: Parser[_]) = and(a, o) + def -(o: Parser[_]) = sub(a, o) + def examples(s: String*): Parser[A] = examples(s.toSet) + def examples(s: Set[String], check: Boolean = false): Parser[A] = examples(new FixedSetExamples(s), s.size, check) + def examples(s: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] = Parser.examples(a, s, maxNumberOfExamples, removeInvalidExamples) + def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg) + def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString) + def flatMap[B](f: A => Parser[B]) = bindParser(a, f) + } + + implicit def literalRichCharParser(c: Char): RichParser[Char] = richParser(c) + implicit def literalRichStringParser(s: String): RichParser[String] = richParser(s) + + /** + * Construct a parser that is valid, but has no valid result. This is used as a way + * to provide a definitive Failure when a parser doesn't match empty input. For example, + * in `softFailure(...) | p`, if `p` doesn't match the empty sequence, the failure will come + * from the Parser constructed by the `softFailure` method. + */ + private[sbt] def softFailure(msg: => String, definitive: Boolean = false): Parser[Nothing] = + SoftInvalid(mkFailures(msg :: Nil, definitive)) + + /** + * Defines a parser that always fails on any input with messages `msgs`. + * If `definitive` is `true`, any failures by later alternatives are discarded. + */ + def invalid(msgs: => Seq[String], definitive: Boolean = false): Parser[Nothing] = Invalid(mkFailures(msgs, definitive)) + + /** + * Defines a parser that always fails on any input with message `msg`. + * If `definitive` is `true`, any failures by later alternatives are discarded. + */ + def failure(msg: => String, definitive: Boolean = false): Parser[Nothing] = invalid(msg :: Nil, definitive) + + /** Defines a parser that always succeeds on empty input with the result `value`.*/ + def success[T](value: T): Parser[T] = new ValidParser[T] { + override def result = Some(value) + def resultEmpty = Value(value) + def derive(c: Char) = Parser.failure("Expected end of input.") + def completions(level: Int) = Completions.empty + override def toString = "success(" + value + ")" + } + + /** Presents a Char range as a Parser. A single Char is parsed only if it is in the given range.*/ + implicit def range(r: collection.immutable.NumericRange[Char]): Parser[Char] = + charClass(r contains _).examples(r.map(_.toString): _*) + + /** Defines a Parser that parses a single character only if it is contained in `legal`.*/ + def chars(legal: String): Parser[Char] = + { + val set = legal.toSet + charClass(set, "character in '" + legal + "'") examples (set.map(_.toString)) + } + + /** + * Defines a Parser that parses a single character only if the predicate `f` returns true for that character. + * If this parser fails, `label` is used as the failure message. + */ + def charClass(f: Char => Boolean, label: String = ""): Parser[Char] = new CharacterClass(f, label) + + /** Presents a single Char `ch` as a Parser that only parses that exact character. */ + implicit def literal(ch: Char): Parser[Char] = new ValidParser[Char] { + def result = None + def resultEmpty = mkFailure("Expected '" + ch + "'") + def derive(c: Char) = if (c == ch) success(ch) else new Invalid(resultEmpty) + def completions(level: Int) = Completions.single(Completion.suggestStrict(ch.toString)) + override def toString = "'" + ch + "'" + } + /** Presents a literal String `s` as a Parser that only parses that exact text and provides it as the result.*/ + implicit def literal(s: String): Parser[String] = stringLiteral(s, 0) + + /** See [[unapply]]. */ + object ~ { + /** Convenience for destructuring a tuple that mirrors the `~` combinator.*/ + def unapply[A, B](t: (A, B)): Some[(A, B)] = Some(t) + } + + /** Parses input `str` using `parser`. If successful, the result is provided wrapped in `Right`. If unsuccessful, an error message is provided in `Left`.*/ + def parse[T](str: String, parser: Parser[T]): Either[String, T] = + Parser.result(parser, str).left.map { failures => + val (msgs, pos) = failures() + ProcessError(str, msgs, pos) + } + + /** + * Convenience method to use when developing a parser. + * `parser` is applied to the input `str`. + * If `completions` is true, the available completions for the input are displayed. + * Otherwise, the result of parsing is printed using the result's `toString` method. + * If parsing fails, the error message is displayed. + * + * See also [[sampleParse]] and [[sampleCompletions]]. + */ + def sample(str: String, parser: Parser[_], completions: Boolean = false): Unit = + if (completions) sampleCompletions(str, parser) else sampleParse(str, parser) + + /** + * Convenience method to use when developing a parser. + * `parser` is applied to the input `str` and the result of parsing is printed using the result's `toString` method. + * If parsing fails, the error message is displayed. + */ + def sampleParse(str: String, parser: Parser[_]): Unit = + parse(str, parser) match { + case Left(msg) => println(msg) + case Right(v) => println(v) + } + + /** + * Convenience method to use when developing a parser. + * `parser` is applied to the input `str` and the available completions are displayed on separate lines. + * If parsing fails, the error message is displayed. + */ + def sampleCompletions(str: String, parser: Parser[_], level: Int = 1): Unit = + Parser.completions(parser, str, level).get foreach println + + // intended to be temporary pending proper error feedback + def result[T](p: Parser[T], s: String): Either[() => (Seq[String], Int), T] = + { + def loop(i: Int, a: Parser[T]): Either[() => (Seq[String], Int), T] = + a match { + case Invalid(f) => Left(() => (f.errors, i)) + case _ => + val ci = i + 1 + if (ci >= s.length) + a.resultEmpty.toEither.left.map { msgs0 => + () => + val msgs = msgs0() + val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs + (nonEmpty, ci) + } + else + loop(ci, a derive s(ci)) + } + loop(-1, p) + } + + /** Applies parser `p` to input `s`. */ + def apply[T](p: Parser[T])(s: String): Parser[T] = + (p /: s)(derive1) + + /** Applies parser `p` to a single character of input. */ + def derive1[T](p: Parser[T], c: Char): Parser[T] = + if (p.valid) p.derive(c) else p + + /** + * Applies parser `p` to input `s` and returns the completions at verbosity `level`. + * The interpretation of `level` is up to parser definitions, but 0 is the default by convention, + * with increasing positive numbers corresponding to increasing verbosity. Typically no more than + * a few levels are defined. + */ + def completions(p: Parser[_], s: String, level: Int): Completions = + // The x Completions.empty removes any trailing token completions where append.isEmpty + apply(p)(s).completions(level) x Completions.empty + + def examples[A](a: Parser[A], completions: Set[String], check: Boolean = false): Parser[A] = + examples(a, new FixedSetExamples(completions), completions.size, check) + + /** + * @param a the parser to decorate with a source of examples. All validation and parsing is delegated to this parser, + * only [[Parser.completions]] is modified. + * @param completions the source of examples when displaying completions to the user. + * @param maxNumberOfExamples limits the number of examples that the source of examples should return. This can + * prevent lengthy pauses and avoids bad interactive user experience. + * @param removeInvalidExamples indicates whether completion examples should be checked for validity (against the given parser). An + * exception is thrown if the example source contains no valid completion suggestions. + * @tparam A the type of values that are returned by the parser. + * @return + */ + def examples[A](a: Parser[A], completions: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] = + if (a.valid) { + a.result match { + case Some(av) => success(av) + case None => + new ParserWithExamples(a, completions, maxNumberOfExamples, removeInvalidExamples) + } + } else a + + def matched(t: Parser[_], seen: Vector[Char] = Vector.empty, partial: Boolean = false): Parser[String] = + t match { + case i: Invalid => if (partial && seen.nonEmpty) success(seen.mkString) else i + case _ => + if (t.result.isEmpty) + new MatchedString(t, seen, partial) + else + success(seen.mkString) + } + + /** + * Establishes delegate parser `t` as a single token of tab completion. + * When tab completion of part of this token is requested, the completions provided by the delegate `t` or a later derivative are appended to + * the prefix String already seen by this parser. + */ + def token[T](t: Parser[T]): Parser[T] = token(t, TokenCompletions.default) + + /** + * Establishes delegate parser `t` as a single token of tab completion. + * When tab completion of part of this token is requested, no completions are returned if `hide` returns true for the current tab completion level. + * Otherwise, the completions provided by the delegate `t` or a later derivative are appended to the prefix String already seen by this parser. + */ + def token[T](t: Parser[T], hide: Int => Boolean): Parser[T] = token(t, TokenCompletions.default.hideWhen(hide)) + + /** + * Establishes delegate parser `t` as a single token of tab completion. + * When tab completion of part of this token is requested, `description` is displayed for suggestions and no completions are ever performed. + */ + def token[T](t: Parser[T], description: String): Parser[T] = token(t, TokenCompletions.displayOnly(description)) + + /** + * Establishes delegate parser `t` as a single token of tab completion. + * When tab completion of part of this token is requested, `display` is used as the printed suggestion, but the completions from the delegate + * parser `t` are used to complete if unambiguous. + */ + def tokenDisplay[T](t: Parser[T], display: String): Parser[T] = + token(t, TokenCompletions.overrideDisplay(display)) + + def token[T](t: Parser[T], complete: TokenCompletions): Parser[T] = + mkToken(t, "", complete) + + @deprecated("Use a different `token` overload.", "0.12.1") + def token[T](t: Parser[T], seen: String, track: Boolean, hide: Int => Boolean): Parser[T] = + { + val base = if (track) TokenCompletions.default else TokenCompletions.displayOnly(seen) + token(t, base.hideWhen(hide)) + } + + private[sbt] def mkToken[T](t: Parser[T], seen: String, complete: TokenCompletions): Parser[T] = + if (t.valid && !t.isTokenStart) + if (t.result.isEmpty) new TokenStart(t, seen, complete) else t + else + t + + def homParser[A](a: Parser[A], b: Parser[A]): Parser[A] = (a, b) match { + case (Invalid(af), Invalid(bf)) => Invalid(af ++ bf) + case (Invalid(_), bv) => bv + case (av, Invalid(_)) => av + case (av, bv) => new HomParser(a, b) + } + + @deprecated("Explicitly specify the failure message.", "0.12.2") + def not(p: Parser[_]): Parser[Unit] = not(p, "Excluded.") + + def not(p: Parser[_], failMessage: String): Parser[Unit] = p.result match { + case None => new Not(p, failMessage) + case Some(_) => failure(failMessage) + } + + def oneOf[T](p: Seq[Parser[T]]): Parser[T] = p.reduceLeft(_ | _) + def seq[T](p: Seq[Parser[T]]): Parser[Seq[T]] = seq0(p, Nil) + def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] = + { + val (newErrors, valid) = separate(p) { case Invalid(f) => Left(f.errors); case ok => Right(ok) } + def combinedErrors = errors ++ newErrors.flatten + if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors) + } + + def stringLiteral(s: String, start: Int): Parser[String] = + { + val len = s.length + if (len == 0) sys.error("String literal cannot be empty") else if (start >= len) success(s) else new StringLiteral(s, start) + } +} +sealed trait ValidParser[T] extends Parser[T] { + final def valid = true + final def failure = None + final def ifValid[S](p: => Parser[S]): Parser[S] = p +} +private final case class Invalid(fail: Failure) extends Parser[Nothing] { + def failure = Some(fail) + def result = None + def resultEmpty = fail + def derive(c: Char) = sys.error("Invalid.") + def completions(level: Int) = Completions.nil + override def toString = fail.errors.mkString("; ") + def valid = false + def ifValid[S](p: => Parser[S]): Parser[S] = this +} + +private final case class SoftInvalid(fail: Failure) extends ValidParser[Nothing] { + def result = None + def resultEmpty = fail + def derive(c: Char) = Invalid(fail) + def completions(level: Int) = Completions.nil + override def toString = fail.errors.mkString("; ") +} + +private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A] { + def result = try { a.result } catch { case e: Exception => None } + def resultEmpty = try { a.resultEmpty } catch { case e: Exception => fail(e) } + def derive(c: Char) = try { trapAndFail(a derive c) } catch { case e: Exception => Invalid(fail(e)) } + def completions(level: Int) = try { a.completions(level) } catch { case e: Exception => Completions.nil } + override def toString = "trap(" + a + ")" + override def isTokenStart = a.isTokenStart + private[this] def fail(e: Exception): Failure = mkFailure(e.toString) +} + +private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A] { + def result = a.result + def resultEmpty = a.resultEmpty match { case f: Failure => mkFailure(message); case v: Value[A] => v } + def derive(c: Char) = onFailure(a derive c, message) + def completions(level: Int) = a.completions(level) + override def toString = "(" + a + " !!! \"" + message + "\" )" + override def isTokenStart = a.isTokenStart +} +private final class SeqParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[(A, B)] { + lazy val result = tuple(a.result, b.result) + lazy val resultEmpty = a.resultEmpty seq b.resultEmpty + def derive(c: Char) = + { + val common = a.derive(c) ~ b + a.resultEmpty match { + case Value(av) => common | b.derive(c).map(br => (av, br)) + case _: Failure => common + } + } + def completions(level: Int) = a.completions(level) x b.completions(level) + override def toString = "(" + a + " ~ " + b + ")" +} + +private final class HomParser[A](a: Parser[A], b: Parser[A]) extends ValidParser[A] { + lazy val result = tuple(a.result, b.result) map (_._1) + def derive(c: Char) = (a derive c) | (b derive c) + lazy val resultEmpty = a.resultEmpty or b.resultEmpty + def completions(level: Int) = a.completions(level) ++ b.completions(level) + override def toString = "(" + a + " | " + b + ")" +} +private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[Either[A, B]] { + lazy val result = tuple(a.result, b.result) map { case (a, b) => Left(a) } + def derive(c: Char) = (a derive c) || (b derive c) + lazy val resultEmpty = a.resultEmpty either b.resultEmpty + def completions(level: Int) = a.completions(level) ++ b.completions(level) + override def toString = "(" + a + " || " + b + ")" +} +private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) extends ValidParser[Seq[T]] { + assert(a.nonEmpty) + lazy val resultEmpty: Result[Seq[T]] = + { + val res = a.map(_.resultEmpty) + val (failures, values) = separate(res)(_.toEither) + // if(failures.isEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) + if (values.nonEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) + } + def result = { + val success = a.flatMap(_.result) + if (success.length == a.length) Some(success) else None + } + def completions(level: Int) = a.map(_.completions(level)).reduceLeft(_ ++ _) + def derive(c: Char) = seq0(a.map(_ derive c), errors) + override def toString = "seq(" + a + ")" +} + +private final class BindParser[A, B](a: Parser[A], f: A => Parser[B]) extends ValidParser[B] { + lazy val result = a.result flatMap { av => f(av).result } + lazy val resultEmpty = a.resultEmpty flatMap { av => f(av).resultEmpty } + def completions(level: Int) = + a.completions(level) flatMap { c => + apply(a)(c.append).resultEmpty match { + case _: Failure => Completions.strict(Set.empty + c) + case Value(av) => c x f(av).completions(level) + } + } + + def derive(c: Char) = + { + val common = a derive c flatMap f + a.resultEmpty match { + case Value(av) => common | derive1(f(av), c) + case _: Failure => common + } + } + override def isTokenStart = a.isTokenStart + override def toString = "bind(" + a + ")" +} +private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser[B] { + lazy val result = a.result map f + lazy val resultEmpty = a.resultEmpty map f + def derive(c: Char) = (a derive c) map f + def completions(level: Int) = a.completions(level) + override def isTokenStart = a.isTokenStart + override def toString = "map(" + a + ")" +} +private final class Filter[T](p: Parser[T], f: T => Boolean, seen: String, msg: String => String) extends ValidParser[T] { + def filterResult(r: Result[T]) = r.filter(f, msg(seen)) + lazy val result = p.result filter f + lazy val resultEmpty = filterResult(p.resultEmpty) + def derive(c: Char) = filterParser(p derive c, f, seen + c, msg) + def completions(level: Int) = p.completions(level) filterS { s => filterResult(apply(p)(s).resultEmpty).isValid } + override def toString = "filter(" + p + ")" + override def isTokenStart = p.isTokenStart +} +private final class MatchedString(delegate: Parser[_], seenV: Vector[Char], partial: Boolean) extends ValidParser[String] { + lazy val seen = seenV.mkString + def derive(c: Char) = matched(delegate derive c, seenV :+ c, partial) + def completions(level: Int) = delegate.completions(level) + def result = if (delegate.result.isDefined) Some(seen) else None + def resultEmpty = delegate.resultEmpty match { case f: Failure if !partial => f; case _ => Value(seen) } + override def isTokenStart = delegate.isTokenStart + override def toString = "matched(" + partial + ", " + seen + ", " + delegate + ")" +} +private final class TokenStart[T](delegate: Parser[T], seen: String, complete: TokenCompletions) extends ValidParser[T] { + def derive(c: Char) = mkToken(delegate derive c, seen + c, complete) + def completions(level: Int) = complete match { + case dc: TokenCompletions.Delegating => dc.completions(seen, level, delegate.completions(level)) + case fc: TokenCompletions.Fixed => fc.completions(seen, level) + } + def result = delegate.result + def resultEmpty = delegate.resultEmpty + override def isTokenStart = true + override def toString = "token('" + complete + ", " + delegate + ")" +} +private final class And[T](a: Parser[T], b: Parser[_]) extends ValidParser[T] { + lazy val result = tuple(a.result, b.result) map { _._1 } + def derive(c: Char) = (a derive c) & (b derive c) + def completions(level: Int) = a.completions(level).filterS(s => apply(b)(s).resultEmpty.isValid) + lazy val resultEmpty = a.resultEmpty && b.resultEmpty + override def toString = "(%s) && (%s)".format(a, b) +} + +private final class Not(delegate: Parser[_], failMessage: String) extends ValidParser[Unit] { + def derive(c: Char) = if (delegate.valid) not(delegate derive c, failMessage) else this + def completions(level: Int) = Completions.empty + def result = None + lazy val resultEmpty = delegate.resultEmpty match { + case f: Failure => Value(()) + case v: Value[_] => mkFailure(failMessage) + } + override def toString = " -(%s)".format(delegate) +} + +/** + * This class wraps an existing parser (the delegate), and replaces the delegate's completions with examples from + * the given example source. + * + * This class asks the example source for a limited amount of examples (to prevent lengthy and expensive + * computations and large amounts of allocated data). It then passes these examples on to the UI. + * + * @param delegate the parser to decorate with completion examples (i.e., completion of user input). + * @param exampleSource the source from which this class will take examples (potentially filter them with the delegate + * parser), and pass them to the UI. + * @param maxNumberOfExamples the maximum number of completions to read from the example source and pass to the UI. This + * limit prevents lengthy example generation and allocation of large amounts of memory. + * @param removeInvalidExamples indicates whether to remove examples that are deemed invalid by the delegate parser. + * @tparam T the type of value produced by the parser. + */ +private final class ParserWithExamples[T](delegate: Parser[T], exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean) extends ValidParser[T] { + def derive(c: Char) = + examples(delegate derive c, exampleSource.withAddedPrefix(c.toString), maxNumberOfExamples, removeInvalidExamples) + + def result = delegate.result + + lazy val resultEmpty = delegate.resultEmpty + + def completions(level: Int) = { + if (exampleSource().isEmpty) + if (resultEmpty.isValid) Completions.nil else Completions.empty + else { + val examplesBasedOnTheResult = filteredExamples.take(maxNumberOfExamples).toSet + Completions(examplesBasedOnTheResult.map(ex => Completion.suggestion(ex))) + } + } + + override def toString = "examples(" + delegate + ", " + exampleSource().take(2).toList + ")" + + private def filteredExamples: Iterable[String] = { + if (removeInvalidExamples) + exampleSource().filter(isExampleValid) + else + exampleSource() + } + + private def isExampleValid(example: String): Boolean = { + apply(delegate)(example).resultEmpty.isValid + } +} +private final class StringLiteral(str: String, start: Int) extends ValidParser[String] { + assert(0 <= start && start < str.length) + def failMsg = "Expected '" + str + "'" + def resultEmpty = mkFailure(failMsg) + def result = None + def derive(c: Char) = if (str.charAt(start) == c) stringLiteral(str, start + 1) else new Invalid(resultEmpty) + def completions(level: Int) = Completions.single(Completion.suggestion(str.substring(start))) + override def toString = '"' + str + '"' +} +private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char] { + def result = None + def resultEmpty = mkFailure("Expected " + label) + def derive(c: Char) = if (f(c)) success(c) else Invalid(resultEmpty) + def completions(level: Int) = Completions.empty + override def toString = "class(" + label + ")" +} +private final class Optional[T](delegate: Parser[T]) extends ValidParser[Option[T]] { + def result = delegate.result map some.fn + def resultEmpty = Value(None) + def derive(c: Char) = (delegate derive c).map(some.fn) + def completions(level: Int) = Completion.empty +: delegate.completions(level) + override def toString = delegate.toString + "?" +} +private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], min: Int, max: UpperBound, accumulatedReverse: List[T]) extends ValidParser[Seq[T]] { + assume(0 <= min, "Minimum occurences must be non-negative") + assume(max >= min, "Minimum occurences must be less than the maximum occurences") + + def derive(c: Char) = + partial match { + case Some(part) => + val partD = repeat(Some(part derive c), repeated, min, max, accumulatedReverse) + part.resultEmpty match { + case Value(pv) => partD | repeatDerive(c, pv :: accumulatedReverse) + case _: Failure => partD + } + case None => repeatDerive(c, accumulatedReverse) + } + + def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = repeat(Some(repeated derive c), repeated, (min - 1) max 0, max.decrement, accRev) + + def completions(level: Int) = + { + def pow(comp: Completions, exp: Completions, n: Int): Completions = + if (n == 1) comp else pow(comp x exp, exp, n - 1) + + val repC = repeated.completions(level) + val fin = if (min == 0) Completion.empty +: repC else pow(repC, repC, min) + partial match { + case Some(p) => p.completions(level) x fin + case None => fin + } + } + def result = None + lazy val resultEmpty: Result[Seq[T]] = + { + val partialAccumulatedOption = + partial match { + case None => Value(accumulatedReverse) + case Some(partialPattern) => partialPattern.resultEmpty.map(_ :: accumulatedReverse) + } + (partialAccumulatedOption app repeatedParseEmpty)(_ reverse_::: _) + } + private def repeatedParseEmpty: Result[List[T]] = + { + if (min == 0) + Value(Nil) + else + // forced determinism + for (value <- repeated.resultEmpty) yield makeList(min, value) + } + override def toString = "repeat(" + min + "," + max + "," + partial + "," + repeated + ")" +} diff --git a/util/complete/src/main/scala/sbt/complete/Parsers.scala b/util/complete/src/main/scala/sbt/complete/Parsers.scala new file mode 100644 index 000000000..3183929e8 --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/Parsers.scala @@ -0,0 +1,268 @@ +/* sbt -- Simple Build Tool + * Copyright 2011 Mark Harrah + */ +package sbt.complete + +import Parser._ +import java.io.File +import java.net.URI +import java.lang.Character.{ getType, MATH_SYMBOL, OTHER_SYMBOL, DASH_PUNCTUATION, OTHER_PUNCTUATION, MODIFIER_SYMBOL, CURRENCY_SYMBOL } + +/** Provides standard implementations of commonly useful [[Parser]]s. */ +trait Parsers { + /** Matches the end of input, providing no useful result on success. */ + lazy val EOF = not(any) + + /** Parses any single character and provides that character as the result. */ + lazy val any: Parser[Char] = charClass(_ => true, "any character") + + /** Set that contains each digit in a String representation.*/ + lazy val DigitSet = Set("0", "1", "2", "3", "4", "5", "6", "7", "8", "9") + + /** Parses any single digit and provides that digit as a Char as the result.*/ + lazy val Digit = charClass(_.isDigit, "digit") examples DigitSet + + /** Set containing Chars for hexadecimal digits 0-9 and A-F (but not a-f). */ + lazy val HexDigitSet = Set('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F') + + /** Parses a single hexadecimal digit (0-9, a-f, A-F). */ + lazy val HexDigit = charClass(c => HexDigitSet(c.toUpper), "hex digit") examples HexDigitSet.map(_.toString) + + /** Parses a single letter, according to Char.isLetter, into a Char. */ + lazy val Letter = charClass(_.isLetter, "letter") + + /** Parses the first Char in an sbt identifier, which must be a [[Letter]].*/ + def IDStart = Letter + + /** Parses an identifier Char other than the first character. This includes letters, digits, dash `-`, and underscore `_`.*/ + lazy val IDChar = charClass(isIDChar, "ID character") + + /** Parses an identifier String, which must start with [[IDStart]] and contain zero or more [[IDChar]]s after that. */ + lazy val ID = identifier(IDStart, IDChar) + + /** Parses a single operator Char, as allowed by [[isOpChar]]. */ + lazy val OpChar = charClass(isOpChar, "symbol") + + /** Parses a non-empty operator String, which consists only of characters allowed by [[OpChar]]. */ + lazy val Op = OpChar.+.string + + /** Parses either an operator String defined by [[Op]] or a non-symbolic identifier defined by [[ID]]. */ + lazy val OpOrID = ID | Op + + /** Parses a single, non-symbolic Scala identifier Char. Valid characters are letters, digits, and the underscore character `_`. */ + lazy val ScalaIDChar = charClass(isScalaIDChar, "Scala identifier character") + + /** Parses a non-symbolic Scala-like identifier. The identifier must start with [[IDStart]] and contain zero or more [[ScalaIDChar]]s after that.*/ + lazy val ScalaID = identifier(IDStart, ScalaIDChar) + + /** Parses a String that starts with `start` and is followed by zero or more characters parsed by `rep`.*/ + def identifier(start: Parser[Char], rep: Parser[Char]): Parser[String] = + start ~ rep.* map { case x ~ xs => (x +: xs).mkString } + + def opOrIDSpaced(s: String): Parser[Char] = + if (DefaultParsers.matches(ID, s)) + OpChar | SpaceClass + else if (DefaultParsers.matches(Op, s)) + IDChar | SpaceClass + else + any + + /** Returns true if `c` an operator character. */ + def isOpChar(c: Char) = !isDelimiter(c) && isOpType(getType(c)) + def isOpType(cat: Int) = cat match { case MATH_SYMBOL | OTHER_SYMBOL | DASH_PUNCTUATION | OTHER_PUNCTUATION | MODIFIER_SYMBOL | CURRENCY_SYMBOL => true; case _ => false } + /** Returns true if `c` is a dash `-`, a letter, digit, or an underscore `_`. */ + def isIDChar(c: Char) = isScalaIDChar(c) || c == '-' + + /** Returns true if `c` is a letter, digit, or an underscore `_`. */ + def isScalaIDChar(c: Char) = c.isLetterOrDigit || c == '_' + + def isDelimiter(c: Char) = c match { case '`' | '\'' | '\"' | /*';' | */ ',' | '.' => true; case _ => false } + + /** Matches a single character that is not a whitespace character. */ + lazy val NotSpaceClass = charClass(!_.isWhitespace, "non-whitespace character") + + /** Matches a single whitespace character, as determined by Char.isWhitespace.*/ + lazy val SpaceClass = charClass(_.isWhitespace, "whitespace character") + + /** Matches a non-empty String consisting of non-whitespace characters. */ + lazy val NotSpace = NotSpaceClass.+.string + + /** Matches a possibly empty String consisting of non-whitespace characters. */ + lazy val OptNotSpace = NotSpaceClass.*.string + + /** + * Matches a non-empty String consisting of whitespace characters. + * The suggested tab completion is a single, constant space character. + */ + lazy val Space = SpaceClass.+.examples(" ") + + /** + * Matches a possibly empty String consisting of whitespace characters. + * The suggested tab completion is a single, constant space character. + */ + lazy val OptSpace = SpaceClass.*.examples(" ") + + /** Parses a non-empty String that contains only valid URI characters, as defined by [[URIChar]].*/ + lazy val URIClass = URIChar.+.string !!! "Invalid URI" + + /** Triple-quotes, as used for verbatim quoting.*/ + lazy val VerbatimDQuotes = "\"\"\"" + + /** Double quote character. */ + lazy val DQuoteChar = '\"' + + /** Backslash character. */ + lazy val BackslashChar = '\\' + + /** Matches a single double quote. */ + lazy val DQuoteClass = charClass(_ == DQuoteChar, "double-quote character") + + /** Matches any character except a double quote or whitespace. */ + lazy val NotDQuoteSpaceClass = + charClass({ c: Char => (c != DQuoteChar) && !c.isWhitespace }, "non-double-quote-space character") + + /** Matches any character except a double quote or backslash. */ + lazy val NotDQuoteBackslashClass = + charClass({ c: Char => (c != DQuoteChar) && (c != BackslashChar) }, "non-double-quote-backslash character") + + /** Matches a single character that is valid somewhere in a URI. */ + lazy val URIChar = charClass(alphanum) | chars("_-!.~'()*,;:$&+=?/[]@%#") + + /** Returns true if `c` is an ASCII letter or digit. */ + def alphanum(c: Char) = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') + + /** + * @param base the directory used for completion proposals (when the user presses the TAB key). Only paths under this + * directory will be proposed. + * @return the file that was parsed from the input string. The returned path may or may not exist. + */ + def fileParser(base: File): Parser[File] = + OptSpace ~> StringBasic + .examples(new FileExamples(base)) + .map(new File(_)) + + /** Parses a port number. Currently, this accepts any integer and presents a tab completion suggestion of ``. */ + lazy val Port = token(IntBasic, "") + + /** Parses a signed integer. */ + lazy val IntBasic = mapOrFail('-'.? ~ Digit.+)(Function.tupled(toInt)) + + /** Parses an unsigned integer. */ + lazy val NatBasic = mapOrFail(Digit.+)(_.mkString.toInt) + + private[this] def toInt(neg: Option[Char], digits: Seq[Char]): Int = + (neg.toSeq ++ digits).mkString.toInt + + /** Parses the lower-case values `true` and `false` into their respesct Boolean values. */ + lazy val Bool = ("true" ^^^ true) | ("false" ^^^ false) + + /** + * Parses a potentially quoted String value. The value may be verbatim quoted ([[StringVerbatim]]), + * quoted with interpreted escapes ([[StringEscapable]]), or unquoted ([[NotQuoted]]). + */ + lazy val StringBasic = StringVerbatim | StringEscapable | NotQuoted + + /** + * Parses a verbatim quoted String value, discarding the quotes in the result. This kind of quoted text starts with triple quotes `"""` + * and ends at the next triple quotes and may contain any character in between. + */ + lazy val StringVerbatim: Parser[String] = VerbatimDQuotes ~> + any.+.string.filter(!_.contains(VerbatimDQuotes), _ => "Invalid verbatim string") <~ + VerbatimDQuotes + + /** + * Parses a string value, interpreting escapes and discarding the surrounding quotes in the result. + * See [[EscapeSequence]] for supported escapes. + */ + lazy val StringEscapable: Parser[String] = + (DQuoteChar ~> (NotDQuoteBackslashClass | EscapeSequence).+.string <~ DQuoteChar | + (DQuoteChar ~ DQuoteChar) ^^^ "") + + /** + * Parses a single escape sequence into the represented Char. + * Escapes start with a backslash and are followed by `u` for a [[UnicodeEscape]] or by `b`, `t`, `n`, `f`, `r`, `"`, `'`, `\` for standard escapes. + */ + lazy val EscapeSequence: Parser[Char] = + BackslashChar ~> ('b' ^^^ '\b' | 't' ^^^ '\t' | 'n' ^^^ '\n' | 'f' ^^^ '\f' | 'r' ^^^ '\r' | + '\"' ^^^ '\"' | '\'' ^^^ '\'' | '\\' ^^^ '\\' | UnicodeEscape) + + /** + * Parses a single unicode escape sequence into the represented Char. + * A unicode escape begins with a backslash, followed by a `u` and 4 hexadecimal digits representing the unicode value. + */ + lazy val UnicodeEscape: Parser[Char] = + ("u" ~> repeat(HexDigit, 4, 4)) map { seq => Integer.parseInt(seq.mkString, 16).toChar } + + /** Parses an unquoted, non-empty String value that cannot start with a double quote and cannot contain whitespace.*/ + lazy val NotQuoted = (NotDQuoteSpaceClass ~ OptNotSpace) map { case (c, s) => c.toString + s } + + /** + * Applies `rep` zero or more times, separated by `sep`. + * The result is the (possibly empty) sequence of results from the multiple `rep` applications. The `sep` results are discarded. + */ + def repsep[T](rep: Parser[T], sep: Parser[_]): Parser[Seq[T]] = + rep1sep(rep, sep) ?? Nil + + /** + * Applies `rep` one or more times, separated by `sep`. + * The result is the non-empty sequence of results from the multiple `rep` applications. The `sep` results are discarded. + */ + def rep1sep[T](rep: Parser[T], sep: Parser[_]): Parser[Seq[T]] = + (rep ~ (sep ~> rep).*).map { case (x ~ xs) => x +: xs } + + /** Wraps the result of `p` in `Some`.*/ + def some[T](p: Parser[T]): Parser[Option[T]] = p map { v => Some(v) } + + /** + * Applies `f` to the result of `p`, transforming any exception when evaluating + * `f` into a parse failure with the exception `toString` as the message. + */ + def mapOrFail[S, T](p: Parser[S])(f: S => T): Parser[T] = + p flatMap { s => try { success(f(s)) } catch { case e: Exception => failure(e.toString) } } + + /** + * Parses a space-delimited, possibly empty sequence of arguments. + * The arguments may use quotes and escapes according to [[StringBasic]]. + */ + def spaceDelimited(display: String): Parser[Seq[String]] = (token(Space) ~> token(StringBasic, display)).* <~ SpaceClass.* + + /** Applies `p` and uses `true` as the result if it succeeds and turns failure into a result of `false`. */ + def flag[T](p: Parser[T]): Parser[Boolean] = (p ^^^ true) ?? false + + /** + * Defines a sequence parser where the parser used for each part depends on the previously parsed values. + * `p` is applied to the (possibly empty) sequence of already parsed values to obtain the next parser to use. + * The parsers obtained in this way are separated by `sep`, whose result is discarded and only the sequence + * of values from the parsers returned by `p` is used for the result. + */ + def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] = + { + def loop(acc: Seq[A]): Parser[Seq[A]] = { + val next = (sep ~> p(acc)) flatMap { result => loop(acc :+ result) } + next ?? acc + } + p(Vector()) flatMap { first => loop(Seq(first)) } + } + + /** Applies String.trim to the result of `p`. */ + def trimmed(p: Parser[String]) = p map { _.trim } + + /** Parses a URI that is valid according to the single argument java.net.URI constructor. */ + lazy val basicUri = mapOrFail(URIClass)(uri => new URI(uri)) + + /** Parses a URI that is valid according to the single argument java.net.URI constructor, using `ex` as tab completion examples. */ + def Uri(ex: Set[URI]) = basicUri examples (ex.map(_.toString)) +} + +/** Provides standard [[Parser]] implementations. */ +object Parsers extends Parsers + +/** Provides common [[Parser]] implementations and helper methods.*/ +object DefaultParsers extends Parsers with ParserMain { + /** Applies parser `p` to input `s` and returns `true` if the parse was successful. */ + def matches(p: Parser[_], s: String): Boolean = + apply(p)(s).resultEmpty.isValid + + /** Returns `true` if `s` parses successfully according to [[ID]].*/ + def validID(s: String): Boolean = matches(ID, s) +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/ProcessError.scala b/util/complete/src/main/scala/sbt/complete/ProcessError.scala new file mode 100644 index 000000000..7e6c9794e --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/ProcessError.scala @@ -0,0 +1,29 @@ +package sbt.complete + +object ProcessError { + def apply(command: String, msgs: Seq[String], index: Int): String = + { + val (line, modIndex) = extractLine(command, index) + val point = pointerSpace(command, modIndex) + msgs.mkString("\n") + "\n" + line + "\n" + point + "^" + } + def extractLine(s: String, i: Int): (String, Int) = + { + val notNewline = (c: Char) => c != '\n' && c != '\r' + val left = takeRightWhile(s.substring(0, i))(notNewline) + val right = s substring i takeWhile notNewline + (left + right, left.length) + } + def takeRightWhile(s: String)(pred: Char => Boolean): String = + { + def loop(i: Int): String = + if (i < 0) + s + else if (pred(s(i))) + loop(i - 1) + else + s.substring(i + 1) + loop(s.length - 1) + } + def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString; +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/TokenCompletions.scala b/util/complete/src/main/scala/sbt/complete/TokenCompletions.scala new file mode 100644 index 000000000..96e70d2f1 --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/TokenCompletions.scala @@ -0,0 +1,37 @@ +package sbt.complete + +import Completion.{ displayStrict, token => ctoken, tokenDisplay } + +sealed trait TokenCompletions { + def hideWhen(f: Int => Boolean): TokenCompletions +} +object TokenCompletions { + private[sbt] abstract class Delegating extends TokenCompletions { outer => + def completions(seen: String, level: Int, delegate: Completions): Completions + final def hideWhen(hide: Int => Boolean): TokenCompletions = new Delegating { + def completions(seen: String, level: Int, delegate: Completions): Completions = + if (hide(level)) Completions.nil else outer.completions(seen, level, delegate) + } + } + private[sbt] abstract class Fixed extends TokenCompletions { outer => + def completions(seen: String, level: Int): Completions + final def hideWhen(hide: Int => Boolean): TokenCompletions = new Fixed { + def completions(seen: String, level: Int) = + if (hide(level)) Completions.nil else outer.completions(seen, level) + } + } + + val default: TokenCompletions = mapDelegateCompletions((seen, level, c) => ctoken(seen, c.append)) + + def displayOnly(msg: String): TokenCompletions = new Fixed { + def completions(seen: String, level: Int) = Completions.single(displayStrict(msg)) + } + def overrideDisplay(msg: String): TokenCompletions = mapDelegateCompletions((seen, level, c) => tokenDisplay(display = msg, append = c.append)) + + def fixed(f: (String, Int) => Completions): TokenCompletions = new Fixed { + def completions(seen: String, level: Int) = f(seen, level) + } + def mapDelegateCompletions(f: (String, Int, Completion) => Completion): TokenCompletions = new Delegating { + def completions(seen: String, level: Int, delegate: Completions) = Completions(delegate.get.map(c => f(seen, level, c))) + } +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/TypeString.scala b/util/complete/src/main/scala/sbt/complete/TypeString.scala new file mode 100644 index 000000000..6bf89ac05 --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/TypeString.scala @@ -0,0 +1,79 @@ +package sbt.complete + +import DefaultParsers._ +import TypeString._ + +/** + * Basic representation of types parsed from Manifest.toString. + * This can only represent the structure of parameterized types. + * All other types are represented by a TypeString with an empty `args`. + */ +private[sbt] final class TypeString(val base: String, val args: List[TypeString]) { + override def toString = + if (base.startsWith(FunctionName)) + args.dropRight(1).mkString("(", ",", ")") + " => " + args.last + else if (base.startsWith(TupleName)) + args.mkString("(", ",", ")") + else + cleanupTypeName(base) + (if (args.isEmpty) "" else args.mkString("[", ",", "]")) +} + +private[sbt] object TypeString { + /** Makes the string representation of a type as returned by Manifest.toString more readable.*/ + def cleanup(typeString: String): String = + parse(typeString, typeStringParser) match { + case Right(ts) => ts.toString + case Left(err) => typeString + } + + /** + * Makes a fully qualified type name provided by Manifest.toString more readable. + * The argument should be just a name (like scala.Tuple2) and not a full type (like scala.Tuple2[Int,Boolean]) + */ + def cleanupTypeName(base: String): String = + dropPrefix(base).replace('$', '.') + + /** + * Removes prefixes from a fully qualified type name that are unnecessary in the presence of standard imports for an sbt setting. + * This does not use the compiler and is therefore a conservative approximation. + */ + def dropPrefix(base: String): String = + if (base.startsWith(SbtPrefix)) + base.substring(SbtPrefix.length) + else if (base.startsWith(CollectionPrefix)) { + val simple = base.substring(CollectionPrefix.length) + if (ShortenCollection(simple)) simple else base + } else if (base.startsWith(ScalaPrefix)) + base.substring(ScalaPrefix.length) + else if (base.startsWith(JavaPrefix)) + base.substring(JavaPrefix.length) + else + TypeMap.getOrElse(base, base) + + final val CollectionPrefix = "scala.collection." + final val FunctionName = "scala.Function" + final val TupleName = "scala.Tuple" + final val SbtPrefix = "sbt." + final val ScalaPrefix = "scala." + final val JavaPrefix = "java.lang." + /* scala.collection.X -> X */ + val ShortenCollection = Set("Seq", "List", "Set", "Map", "Iterable") + val TypeMap = Map( + "java.io.File" -> "File", + "java.net.URL" -> "URL", + "java.net.URI" -> "URI" + ) + + /** + * A Parser that extracts basic structure from the string representation of a type from Manifest.toString. + * This is rudimentary and essentially only decomposes the string into names and arguments for parameterized types. + */ + lazy val typeStringParser: Parser[TypeString] = + { + def isFullScalaIDChar(c: Char) = isScalaIDChar(c) || c == '.' || c == '$' + lazy val fullScalaID = identifier(IDStart, charClass(isFullScalaIDChar, "Scala identifier character")) + lazy val tpe: Parser[TypeString] = + for (id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').?) yield new TypeString(id, args.toList.flatten) + tpe + } +} \ No newline at end of file diff --git a/util/complete/src/main/scala/sbt/complete/UpperBound.scala b/util/complete/src/main/scala/sbt/complete/UpperBound.scala new file mode 100644 index 000000000..66a32e1a2 --- /dev/null +++ b/util/complete/src/main/scala/sbt/complete/UpperBound.scala @@ -0,0 +1,47 @@ +/* sbt -- Simple Build Tool + * Copyright 2008,2010 Mark Harrah + */ +package sbt.complete + +sealed trait UpperBound { + /** True if and only if the given value meets this bound.*/ + def >=(min: Int): Boolean + /** True if and only if this bound is one.*/ + def isOne: Boolean + /** True if and only if this bound is zero.*/ + def isZero: Boolean + /** + * If this bound is zero or Infinite, `decrement` returns this bound. + * Otherwise, this bound is finite and greater than zero and `decrement` returns the bound that is one less than this bound. + */ + def decrement: UpperBound + /** True if and only if this is unbounded.*/ + def isInfinite: Boolean +} +/** Represents unbounded. */ +case object Infinite extends UpperBound { + /** All finite numbers meet this bound. */ + def >=(min: Int) = true + def isOne = false + def isZero = false + def decrement = this + def isInfinite = true + override def toString = "Infinity" +} +/** + * Represents a finite upper bound. The maximum allowed value is 'value', inclusive. + * It must positive. + */ +final case class Finite(value: Int) extends UpperBound { + assume(value >= 0, "Maximum occurences must be nonnegative.") + + def >=(min: Int) = value >= min + def isOne = value == 1 + def isZero = value == 0 + def decrement = Finite((value - 1) max 0) + def isInfinite = false + override def toString = value.toString +} +object UpperBound { + implicit def intToFinite(i: Int): Finite = Finite(i) +} \ No newline at end of file diff --git a/util/complete/src/test/scala/ParserTest.scala b/util/complete/src/test/scala/ParserTest.scala new file mode 100644 index 000000000..f02431e53 --- /dev/null +++ b/util/complete/src/test/scala/ParserTest.scala @@ -0,0 +1,148 @@ +package sbt.complete + +object JLineTest { + import DefaultParsers._ + + val one = "blue" | "green" | "black" + val two = token("color" ~> Space) ~> token(one) + val three = token("color" ~> Space) ~> token(ID.examples("blue", "green", "black")) + val four = token("color" ~> Space) ~> token(ID, "") + + val num = token(NatBasic) + val five = (num ~ token("+" | "-") ~ num) <~ token('=') flatMap { + case a ~ "+" ~ b => token((a + b).toString) + case a ~ "-" ~ b => token((a - b).toString) + } + + val parsers = Map("1" -> one, "2" -> two, "3" -> three, "4" -> four, "5" -> five) + def main(args: Array[String]): Unit = { + import jline.TerminalFactory + import jline.console.ConsoleReader + val reader = new ConsoleReader() + TerminalFactory.get.init + + val parser = parsers(args(0)) + JLineCompletion.installCustomCompletor(reader, parser) + def loop(): Unit = { + val line = reader.readLine("> ") + if (line ne null) { + println("Result: " + apply(parser)(line).resultEmpty) + loop() + } + } + loop() + } +} + +import Parser._ +import org.scalacheck._ + +object ParserTest extends Properties("Completing Parser") { + import Parsers._ + import DefaultParsers.matches + + val nested = (token("a1") ~ token("b2")) ~ "c3" + val nestedDisplay = (token("a1", "") ~ token("b2", "")) ~ "c3" + + val spacePort = token(Space) ~> Port + + def p[T](f: T): T = { println(f); f } + + def checkSingle(in: String, expect: Completion)(expectDisplay: Completion = expect) = + (("token '" + in + "'") |: checkOne(in, nested, expect)) && + (("display '" + in + "'") |: checkOne(in, nestedDisplay, expectDisplay)) + + def checkOne(in: String, parser: Parser[_], expect: Completion): Prop = + completions(parser, in, 1) == Completions.single(expect) + + def checkAll(in: String, parser: Parser[_], expect: Completions): Prop = + { + val cs = completions(parser, in, 1) + ("completions: " + cs) |: ("Expected: " + expect) |: (cs == expect: Prop) + } + + def checkInvalid(in: String) = + (("token '" + in + "'") |: checkInv(in, nested)) && + (("display '" + in + "'") |: checkInv(in, nestedDisplay)) + + def checkInv(in: String, parser: Parser[_]): Prop = + { + val cs = completions(parser, in, 1) + ("completions: " + cs) |: (cs == Completions.nil: Prop) + } + + property("nested tokens a") = checkSingle("", Completion.token("", "a1"))(Completion.displayOnly("")) + property("nested tokens a1") = checkSingle("a", Completion.token("a", "1"))(Completion.displayOnly("")) + property("nested tokens a inv") = checkInvalid("b") + property("nested tokens b") = checkSingle("a1", Completion.token("", "b2"))(Completion.displayOnly("")) + property("nested tokens b2") = checkSingle("a1b", Completion.token("b", "2"))(Completion.displayOnly("")) + property("nested tokens b inv") = checkInvalid("a1a") + property("nested tokens c") = checkSingle("a1b2", Completion.suggestion("c3"))() + property("nested tokens c3") = checkSingle("a1b2c", Completion.suggestion("3"))() + property("nested tokens c inv") = checkInvalid("a1b2a") + + property("suggest space") = checkOne("", spacePort, Completion.token("", " ")) + property("suggest port") = checkOne(" ", spacePort, Completion.displayOnly("")) + property("no suggest at end") = checkOne("asdf", "asdf", Completion.suggestion("")) + property("no suggest at token end") = checkOne("asdf", token("asdf"), Completion.suggestion("")) + property("empty suggest for examples") = checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestion("")) + property("empty suggest for examples token") = checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestion("")) + + val colors = Set("blue", "green", "red") + val base = (seen: Seq[String]) => token(ID examples (colors -- seen)) + val sep = token(Space) + val repeat = repeatDep(base, sep) + def completionStrings(ss: Set[String]): Completions = Completions(ss.map { s => Completion.token("", s) }) + + property("repeatDep no suggestions for bad input") = checkInv(".", repeat) + property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors)) + property("repeatDep suggest remaining two") = { + val first = colors.toSeq.head + checkAll(first + " ", repeat, completionStrings(colors - first)) + } + property("repeatDep suggest remaining one") = { + val take = colors.toSeq.take(2) + checkAll(take.mkString("", " ", " "), repeat, completionStrings(colors -- take)) + } + property("repeatDep requires at least one token") = !matches(repeat, "") + property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head) + property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) +} +object ParserExample { + val ws = charClass(_.isWhitespace)+ + val notws = charClass(!_.isWhitespace)+ + + val name = token("test") + val options = (ws ~> token("quick" | "failed" | "new"))* + val exampleSet = Set("am", "is", "are", "was", "were") + val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false)))* + + val t = name ~ options ~ include + + // Get completions for some different inputs + println(completions(t, "te", 1)) + println(completions(t, "test ", 1)) + println(completions(t, "test w", 1)) + + // Get the parsed result for different inputs + println(apply(t)("te").resultEmpty) + println(apply(t)("test").resultEmpty) + println(apply(t)("test w").resultEmpty) + println(apply(t)("test was were").resultEmpty) + + def run(n: Int): Unit = { + val a = 'a'.id + val aq = a.? + val aqn = repeat(aq, min = n, max = n) + val an = repeat(a, min = n, max = n) + val ann = aqn ~ an + + def r = apply(ann)("a" * (n * 2)).resultEmpty + println(r.isValid) + } + def run2(n: Int): Unit = { + val ab = "ab".?.* + val r = apply(ab)("a" * n).resultEmpty + println(r) + } +} diff --git a/util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala new file mode 100644 index 000000000..effd9be78 --- /dev/null +++ b/util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -0,0 +1,89 @@ +package sbt.complete + +import org.specs2.mutable.Specification +import org.specs2.specification.Scope +import sbt.IO.withTemporaryDirectory +import java.io.File +import sbt.IO._ + +class FileExamplesTest extends Specification { + + "listing all files in an absolute base directory" should { + "produce the entire base directory's contents" in new directoryStructure { + fileExamples().toList should containTheSameElementsAs(allRelativizedPaths) + } + } + + "listing files with a prefix that matches none" should { + "produce an empty list" in new directoryStructure(withCompletionPrefix = "z") { + fileExamples().toList should beEmpty + } + } + + "listing single-character prefixed files" should { + "produce matching paths only" in new directoryStructure(withCompletionPrefix = "f") { + fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) + } + } + + "listing directory-prefixed files" should { + "produce matching paths only" in new directoryStructure(withCompletionPrefix = "far") { + fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) + } + + "produce sub-dir contents only when appending a file separator to the directory" in new directoryStructure(withCompletionPrefix = "far" + File.separator) { + fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) + } + } + + "listing files with a sub-path prefix" should { + "produce matching paths only" in new directoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { + fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) + } + } + + "completing a full path" should { + "produce a list with an empty string" in new directoryStructure(withCompletionPrefix = "bazaar") { + fileExamples().toList shouldEqual List("") + } + } + + class directoryStructure(withCompletionPrefix: String = "") extends Scope with DelayedInit { + var fileExamples: FileExamples = _ + var baseDir: File = _ + var childFiles: List[File] = _ + var childDirectories: List[File] = _ + var nestedFiles: List[File] = _ + var nestedDirectories: List[File] = _ + + def allRelativizedPaths: List[String] = + (childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories).map(relativize(baseDir, _).get) + + def prefixedPathsOnly: List[String] = + allRelativizedPaths.filter(_ startsWith withCompletionPrefix).map(_ substring withCompletionPrefix.length) + + override def delayedInit(testBody: => Unit): Unit = { + withTemporaryDirectory { + tempDir => + createSampleDirStructure(tempDir) + fileExamples = new FileExamples(baseDir, withCompletionPrefix) + testBody + } + } + + private def createSampleDirStructure(tempDir: File): Unit = { + childFiles = toChildFiles(tempDir, List("foo", "bar", "bazaar")) + childDirectories = toChildFiles(tempDir, List("moo", "far")) + nestedFiles = toChildFiles(childDirectories(1), List("farfile1", "barfile2")) + nestedDirectories = toChildFiles(childDirectories(1), List("fardir1", "bardir2")) + + (childDirectories ++ nestedDirectories).map(_.mkdirs()) + (childFiles ++ nestedFiles).map(_.createNewFile()) + + baseDir = tempDir + } + + private def toChildFiles(baseDir: File, files: List[String]): List[File] = files.map(new File(baseDir, _)) + } + +} diff --git a/util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala b/util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala new file mode 100644 index 000000000..b5aa14250 --- /dev/null +++ b/util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala @@ -0,0 +1,26 @@ +package sbt.complete + +import org.specs2.mutable.Specification +import org.specs2.specification.Scope + +class FixedSetExamplesTest extends Specification { + + "adding a prefix" should { + "produce a smaller set of examples with the prefix removed" in new examples { + fixedSetExamples.withAddedPrefix("f")() must containTheSameElementsAs(List("oo", "ool", "u")) + fixedSetExamples.withAddedPrefix("fo")() must containTheSameElementsAs(List("o", "ol")) + fixedSetExamples.withAddedPrefix("b")() must containTheSameElementsAs(List("ar")) + } + } + + "without a prefix" should { + "produce the original set" in new examples { + fixedSetExamples() mustEqual exampleSet + } + } + + trait examples extends Scope { + val exampleSet = List("foo", "bar", "fool", "fu") + val fixedSetExamples = FixedSetExamples(exampleSet) + } +} diff --git a/util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala new file mode 100644 index 000000000..dff68803c --- /dev/null +++ b/util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -0,0 +1,93 @@ +package sbt.complete + +import org.specs2.mutable.Specification +import org.specs2.specification.Scope +import Completion._ + +class ParserWithExamplesTest extends Specification { + + "listing a limited number of completions" should { + "grab only the needed number of elements from the iterable source of examples" in new parserWithLazyExamples { + parserWithExamples.completions(0) + examples.size shouldEqual maxNumberOfExamples + } + } + + "listing only valid completions" should { + "use the delegate parser to remove invalid examples" in new parserWithValidExamples { + val validCompletions = Completions(Set( + suggestion("blue"), + suggestion("red") + )) + parserWithExamples.completions(0) shouldEqual validCompletions + } + } + + "listing valid completions in a derived parser" should { + "produce only valid examples that start with the character of the derivation" in new parserWithValidExamples { + val derivedCompletions = Completions(Set( + suggestion("lue") + )) + parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions + } + } + + "listing valid and invalid completions" should { + "produce the entire source of examples" in new parserWithAllExamples { + val completions = Completions(examples.map(suggestion(_)).toSet) + parserWithExamples.completions(0) shouldEqual completions + } + } + + "listing valid and invalid completions in a derived parser" should { + "produce only examples that start with the character of the derivation" in new parserWithAllExamples { + val derivedCompletions = Completions(Set( + suggestion("lue"), + suggestion("lock") + )) + parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions + } + } + + class parserWithLazyExamples extends parser(GrowableSourceOfExamples(), maxNumberOfExamples = 5, removeInvalidExamples = false) + + class parserWithValidExamples extends parser(removeInvalidExamples = true) + + class parserWithAllExamples extends parser(removeInvalidExamples = false) + + case class parser(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), + maxNumberOfExamples: Int = 25, + removeInvalidExamples: Boolean) extends Scope { + + import DefaultParsers._ + + val colorParser = "blue" | "green" | "black" | "red" + val parserWithExamples: Parser[String] = new ParserWithExamples[String]( + colorParser, + FixedSetExamples(examples), + maxNumberOfExamples, + removeInvalidExamples + ) + } + + case class GrowableSourceOfExamples() extends Iterable[String] { + private var numberOfIteratedElements: Int = 0 + + override def iterator: Iterator[String] = { + new Iterator[String] { + var currentElement = 0 + + override def next(): String = { + currentElement += 1 + numberOfIteratedElements = Math.max(currentElement, numberOfIteratedElements) + numberOfIteratedElements.toString + } + + override def hasNext: Boolean = true + } + } + + override def size: Int = numberOfIteratedElements + } + +} diff --git a/util/logic/src/main/scala/sbt/logic/Logic.scala b/util/logic/src/main/scala/sbt/logic/Logic.scala new file mode 100644 index 000000000..856394251 --- /dev/null +++ b/util/logic/src/main/scala/sbt/logic/Logic.scala @@ -0,0 +1,336 @@ +package sbt +package logic + +import scala.annotation.tailrec +import Formula.{ And, True } + +/* +Defines a propositional logic with negation as failure and only allows stratified rule sets (negation must be acyclic) in order to have a unique minimal model. + +For example, this is not allowed: + + p :- not q + + q :- not p +but this is: + + p :- q + + q :- p +as is this: + + p :- q + + q := not r + + + Some useful links: + + https://en.wikipedia.org/wiki/Nonmonotonic_logic + + https://en.wikipedia.org/wiki/Negation_as_failure + + https://en.wikipedia.org/wiki/Propositional_logic + + https://en.wikipedia.org/wiki/Stable_model_semantics + + http://www.w3.org/2005/rules/wg/wiki/negation +*/ + +/** Disjunction (or) of the list of clauses. */ +final case class Clauses(clauses: List[Clause]) { + assert(clauses.nonEmpty, "At least one clause is required.") +} + +/** When the `body` Formula succeeds, atoms in `head` are true. */ +final case class Clause(body: Formula, head: Set[Atom]) + +/** A literal is an [[Atom]] or its [[negation|Negated]]. */ +sealed abstract class Literal extends Formula { + /** The underlying (positive) atom. */ + def atom: Atom + /** Negates this literal.*/ + def unary_! : Literal +} +/** A variable with name `label`. */ +final case class Atom(label: String) extends Literal { + def atom = this + def unary_! : Negated = Negated(this) +} +/** + * A negated atom, in the sense of negation as failure, not logical negation. + * That is, it is true if `atom` is not known/defined. + */ +final case class Negated(atom: Atom) extends Literal { + def unary_! : Atom = atom +} + +/** + * A formula consists of variables, negation, and conjunction (and). + * (Disjunction is not currently included- it is modeled at the level of a sequence of clauses. + * This is less convenient when defining clauses, but is not less powerful.) + */ +sealed abstract class Formula { + /** Constructs a clause that proves `atoms` when this formula is true. */ + def proves(atom: Atom, atoms: Atom*): Clause = Clause(this, (atom +: atoms).toSet) + + /** Constructs a formula that is true iff this formula and `f` are both true.*/ + def &&(f: Formula): Formula = (this, f) match { + case (True, x) => x + case (x, True) => x + case (And(as), And(bs)) => And(as ++ bs) + case (And(as), b: Literal) => And(as + b) + case (a: Literal, And(bs)) => And(bs + a) + case (a: Literal, b: Literal) => And(Set(a, b)) + } +} + +object Formula { + /** A conjunction of literals. */ + final case class And(literals: Set[Literal]) extends Formula { + assert(literals.nonEmpty, "'And' requires at least one literal.") + } + final case object True extends Formula +} + +object Logic { + def reduceAll(clauses: List[Clause], initialFacts: Set[Literal]): Either[LogicException, Matched] = + reduce(Clauses(clauses), initialFacts) + + /** + * Computes the variables in the unique stable model for the program represented by `clauses` and `initialFacts`. + * `clause` may not have any negative feedback (that is, negation is acyclic) + * and `initialFacts` cannot be in the head of any clauses in `clause`. + * These restrictions ensure that the logic program has a unique minimal model. + */ + def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = + { + val (posSeq, negSeq) = separate(initialFacts.toSeq) + val (pos, neg) = (posSeq.toSet, negSeq.toSet) + + val problem = + checkContradictions(pos, neg) orElse + checkOverlap(clauses, pos) orElse + checkAcyclic(clauses) + + problem.toLeft( + reduce0(clauses, initialFacts, Matched.empty) + ) + } + + /** + * Verifies `initialFacts` are not in the head of any `clauses`. + * This avoids the situation where an atom is proved but no clauses prove it. + * This isn't necessarily a problem, but the main sbt use cases expects + * a proven atom to have at least one clause satisfied. + */ + private[this] def checkOverlap(clauses: Clauses, initialFacts: Set[Atom]): Option[InitialOverlap] = { + val as = atoms(clauses) + val initialOverlap = initialFacts.filter(as.inHead) + if (initialOverlap.nonEmpty) Some(new InitialOverlap(initialOverlap)) else None + } + + private[this] def checkContradictions(pos: Set[Atom], neg: Set[Atom]): Option[InitialContradictions] = { + val contradictions = pos intersect neg + if (contradictions.nonEmpty) Some(new InitialContradictions(contradictions)) else None + } + + private[this] def checkAcyclic(clauses: Clauses): Option[CyclicNegation] = { + val deps = dependencyMap(clauses) + val cycle = Dag.findNegativeCycle(graph(deps)) + if (cycle.nonEmpty) Some(new CyclicNegation(cycle)) else None + } + private[this] def graph(deps: Map[Atom, Set[Literal]]) = new Dag.DirectedSignedGraph[Atom] { + type Arrow = Literal + def nodes = deps.keys.toList + def dependencies(a: Atom) = deps.getOrElse(a, Set.empty).toList + def isNegative(b: Literal) = b match { + case Negated(_) => true + case Atom(_) => false + } + def head(b: Literal) = b.atom + } + + private[this] def dependencyMap(clauses: Clauses): Map[Atom, Set[Literal]] = + (Map.empty[Atom, Set[Literal]] /: clauses.clauses) { + case (m, Clause(formula, heads)) => + val deps = literals(formula) + (m /: heads) { (n, head) => n.updated(head, n.getOrElse(head, Set.empty) ++ deps) } + } + + sealed abstract class LogicException(override val toString: String) + final class InitialContradictions(val literals: Set[Atom]) extends LogicException("Initial facts cannot be both true and false:\n\t" + literals.mkString("\n\t")) + final class InitialOverlap(val literals: Set[Atom]) extends LogicException("Initial positive facts cannot be implied by any clauses:\n\t" + literals.mkString("\n\t")) + final class CyclicNegation(val cycle: List[Literal]) extends LogicException("Negation may not be involved in a cycle:\n\t" + cycle.mkString("\n\t")) + + /** Tracks proven atoms in the reverse order they were proved. */ + final class Matched private (val provenSet: Set[Atom], reverseOrdered: List[Atom]) { + def add(atoms: Set[Atom]): Matched = add(atoms.toList) + def add(atoms: List[Atom]): Matched = { + val newOnly = atoms.filterNot(provenSet) + new Matched(provenSet ++ newOnly, newOnly ::: reverseOrdered) + } + def ordered: List[Atom] = reverseOrdered.reverse + override def toString = ordered.map(_.label).mkString("Matched(", ",", ")") + } + object Matched { + val empty = new Matched(Set.empty, Nil) + } + + /** Separates a sequence of literals into `(pos, neg)` atom sequences. */ + private[this] def separate(lits: Seq[Literal]): (Seq[Atom], Seq[Atom]) = Util.separate(lits) { + case a: Atom => Left(a) + case Negated(n) => Right(n) + } + + /** + * Finds clauses that have no body and thus prove their head. + * Returns `(, )`. + */ + private[this] def findProven(c: Clauses): (Set[Atom], List[Clause]) = + { + val (proven, unproven) = c.clauses.partition(_.body == True) + (proven.flatMap(_.head).toSet, unproven) + } + private[this] def keepPositive(lits: Set[Literal]): Set[Atom] = + lits.collect { case a: Atom => a }.toSet + + // precondition: factsToProcess contains no contradictions + @tailrec + private[this] def reduce0(clauses: Clauses, factsToProcess: Set[Literal], state: Matched): Matched = + applyAll(clauses, factsToProcess) match { + case None => // all of the remaining clauses failed on the new facts + state + case Some(applied) => + val (proven, unprovenClauses) = findProven(applied) + val processedFacts = state add keepPositive(factsToProcess) + val newlyProven = proven -- processedFacts.provenSet + val newState = processedFacts add newlyProven + if (unprovenClauses.isEmpty) + newState // no remaining clauses, done. + else { + val unproven = Clauses(unprovenClauses) + val nextFacts: Set[Literal] = if (newlyProven.nonEmpty) newlyProven.toSet else inferFailure(unproven) + reduce0(unproven, nextFacts, newState) + } + } + + /** + * Finds negated atoms under the negation as failure rule and returns them. + * This should be called only after there are no more known atoms to be substituted. + */ + private[this] def inferFailure(clauses: Clauses): Set[Literal] = + { + /* At this point, there is at least one clause and one of the following is the case as the result of the acyclic negation rule: + i. there is at least one variable that occurs in a clause body but not in the head of a clause + ii. there is at least one variable that occurs in the head of a clause and does not transitively depend on a negated variable + In either case, each such variable x cannot be proven true and therefore proves 'not x' (negation as failure, !x in the code). + */ + val allAtoms = atoms(clauses) + val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse) + if (newFacts.nonEmpty) + newFacts + else { + val possiblyTrue = hasNegatedDependency(clauses.clauses, Relation.empty, Relation.empty) + val newlyFalse: Set[Literal] = negated(allAtoms.inHead -- possiblyTrue) + if (newlyFalse.nonEmpty) + newlyFalse + else // should never happen due to the acyclic negation rule + sys.error(s"No progress:\n\tclauses: $clauses\n\tpossibly true: $possiblyTrue") + } + } + + private[this] def negated(atoms: Set[Atom]): Set[Literal] = atoms.map(a => Negated(a)) + + /** + * Computes the set of atoms in `clauses` that directly or transitively take a negated atom as input. + * For example, for the following clauses, this method would return `List(a, d)` : + * a :- b, not c + * d :- a + */ + @tailrec + def hasNegatedDependency(clauses: Seq[Clause], posDeps: Relation[Atom, Atom], negDeps: Relation[Atom, Atom]): List[Atom] = + clauses match { + case Seq() => + // because cycles between positive literals are allowed, this isn't strictly a topological sort + Dag.topologicalSortUnchecked(negDeps._1s)(posDeps.reverse) + case Clause(formula, head) +: tail => + // collect direct positive and negative literals and track them in separate graphs + val (pos, neg) = directDeps(formula) + val (newPos, newNeg) = ((posDeps, negDeps) /: head) { + case ((pdeps, ndeps), d) => + (pdeps + (d, pos), ndeps + (d, neg)) + } + hasNegatedDependency(tail, newPos, newNeg) + } + + /** Computes the `(positive, negative)` literals in `formula`. */ + private[this] def directDeps(formula: Formula): (Seq[Atom], Seq[Atom]) = + Util.separate(literals(formula).toSeq) { + case Negated(a) => Right(a) + case a: Atom => Left(a) + } + private[this] def literals(formula: Formula): Set[Literal] = formula match { + case And(lits) => lits + case l: Literal => Set(l) + case True => Set.empty + } + + /** Computes the atoms in the heads and bodies of the clauses in `clause`. */ + def atoms(cs: Clauses): Atoms = cs.clauses.map(c => Atoms(c.head, atoms(c.body))).reduce(_ ++ _) + + /** Computes the set of all atoms in `formula`. */ + def atoms(formula: Formula): Set[Atom] = formula match { + case And(lits) => lits.map(_.atom) + case Negated(lit) => Set(lit) + case a: Atom => Set(a) + case True => Set() + } + + /** Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses. */ + final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]) { + /** Concatenates this with `as`. */ + def ++(as: Atoms): Atoms = Atoms(inHead ++ as.inHead, inFormula ++ as.inFormula) + /** Atoms that cannot be true because they do not occur in a head. */ + def triviallyFalse: Set[Atom] = inFormula -- inHead + } + + /** + * Applies known facts to `clause`s, deriving a new, possibly empty list of clauses. + * 1. If a fact is in the body of a clause, the derived clause has that fact removed from the body. + * 2. If the negation of a fact is in a body of a clause, that clause fails and is removed. + * 3. If a fact or its negation is in the head of a clause, the derived clause has that fact (or its negation) removed from the head. + * 4. If a head is empty, the clause proves nothing and is removed. + * + * NOTE: empty bodies do not cause a clause to succeed yet. + * All known facts must be applied before this can be done in order to avoid inconsistencies. + * Precondition: no contradictions in `facts` + * Postcondition: no atom in `facts` is present in the result + * Postcondition: No clauses have an empty head + */ + def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = + { + val newClauses = + if (facts.isEmpty) + cs.clauses.filter(_.head.nonEmpty) // still need to drop clauses with an empty head + else + cs.clauses.map(c => applyAll(c, facts)).flatMap(_.toList) + if (newClauses.isEmpty) None else Some(Clauses(newClauses)) + } + + def applyAll(c: Clause, facts: Set[Literal]): Option[Clause] = + { + val atoms = facts.map(_.atom) + val newHead = c.head -- atoms // 3. + if (newHead.isEmpty) // 4. empty head + None + else + substitute(c.body, facts).map(f => Clause(f, newHead)) // 1, 2 + } + + /** Derives the formula that results from substituting `facts` into `formula`. */ + @tailrec + def substitute(formula: Formula, facts: Set[Literal]): Option[Formula] = formula match { + case And(lits) => + def negated(lits: Set[Literal]): Set[Literal] = lits.map(a => !a) + if (lits.exists(negated(facts))) // 2. + None + else { + val newLits = lits -- facts + val newF = if (newLits.isEmpty) True else And(newLits) + Some(newF) // 1. + } + case True => Some(True) + case lit: Literal => // define in terms of And + substitute(And(Set(lit)), facts) + } +} diff --git a/util/logic/src/test/scala/sbt/logic/Test.scala b/util/logic/src/test/scala/sbt/logic/Test.scala new file mode 100644 index 000000000..f62a9e767 --- /dev/null +++ b/util/logic/src/test/scala/sbt/logic/Test.scala @@ -0,0 +1,115 @@ +package sbt +package logic + +import org.scalacheck._ +import Prop.secure +import Logic.{ LogicException, Matched } + +object LogicTest extends Properties("Logic") { + import TestClauses._ + + property("Handles trivial resolution.") = secure(expect(trivial, Set(A))) + property("Handles less trivial resolution.") = secure(expect(lessTrivial, Set(B, A, D))) + property("Handles cycles without negation") = secure(expect(cycles, Set(F, A, B))) + property("Handles basic exclusion.") = secure(expect(excludedPos, Set())) + property("Handles exclusion of head proved by negation.") = secure(expect(excludedNeg, Set())) + // TODO: actually check ordering, probably as part of a check that dependencies are satisifed + property("Properly orders results.") = secure(expect(ordering, Set(B, A, C, E, F))) + property("Detects cyclic negation") = secure( + Logic.reduceAll(badClauses, Set()) match { + case Right(res) => false + case Left(err: Logic.CyclicNegation) => true + case Left(err) => sys.error(s"Expected cyclic error, got: $err") + } + ) + + def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match { + case Left(err) => false + case Right(res) => + val actual = res.provenSet + (actual == expected) || sys.error(s"Expected to prove $expected, but actually proved $actual") + } +} + +object TestClauses { + + val A = Atom("A") + val B = Atom("B") + val C = Atom("C") + val D = Atom("D") + val E = Atom("E") + val F = Atom("F") + val G = Atom("G") + + val clauses = + A.proves(B) :: + A.proves(F) :: + B.proves(F) :: + F.proves(A) :: + (!C).proves(F) :: + D.proves(C) :: + C.proves(D) :: + Nil + + val cycles = Logic.reduceAll(clauses, Set()) + + val badClauses = + A.proves(D) :: + clauses + + val excludedNeg = { + val cs = + (!A).proves(B) :: + Nil + val init = + (!A) :: + (!B) :: + Nil + Logic.reduceAll(cs, init.toSet) + } + + val excludedPos = { + val cs = + A.proves(B) :: + Nil + val init = + A :: + (!B) :: + Nil + Logic.reduceAll(cs, init.toSet) + } + + val trivial = { + val cs = + Formula.True.proves(A) :: + Nil + Logic.reduceAll(cs, Set.empty) + } + + val lessTrivial = { + val cs = + Formula.True.proves(A) :: + Formula.True.proves(B) :: + (A && B && (!C)).proves(D) :: + Nil + Logic.reduceAll(cs, Set()) + } + + val ordering = { + val cs = + E.proves(F) :: + (C && !D).proves(E) :: + (A && B).proves(C) :: + Nil + Logic.reduceAll(cs, Set(A, B)) + } + + def all(): Unit = { + println(s"Cycles: $cycles") + println(s"xNeg: $excludedNeg") + println(s"xPos: $excludedPos") + println(s"trivial: $trivial") + println(s"lessTrivial: $lessTrivial") + println(s"ordering: $ordering") + } +} From 39d66c4602fc0b840d446a284d89951c1e7c1ebf Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 31 Aug 2015 01:51:03 +0200 Subject: [PATCH 04/50] Update IO library to sbt/IO v1.0.0-M1 --- .../src/main/scala/sbt/complete/ExampleSource.scala | 4 ++-- .../src/main/scala/sbt/complete/HistoryCommands.scala | 3 ++- .../src/test/scala/sbt/complete/FileExamplesTest.scala | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala b/internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala index 52d96246b..ab2d39b4c 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala @@ -1,7 +1,7 @@ package sbt.complete import java.io.File -import sbt.IO +import sbt.io.IO /** * These sources of examples are used in parsers for user input completion. An example of such a source is the @@ -56,4 +56,4 @@ class FileExamples(base: File, prefix: String = "") extends ExampleSource { private def dirStartsWithPrefix(relativizedPath: String): Boolean = (relativizedPath startsWith prefix) || (prefix startsWith relativizedPath) -} \ No newline at end of file +} diff --git a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala index 1e124c583..c82ecfa3d 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala @@ -5,6 +5,7 @@ package sbt package complete import java.io.File +import sbt.io.IO object HistoryCommands { val Start = "!" @@ -70,4 +71,4 @@ object HistoryCommands { val actionParser: Parser[complete.History => Option[List[String]]] = Start ~> (help | last | execInt | list | execStr) // execStr must come last -} \ No newline at end of file +} diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index effd9be78..f9cc77038 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -2,9 +2,8 @@ package sbt.complete import org.specs2.mutable.Specification import org.specs2.specification.Scope -import sbt.IO.withTemporaryDirectory import java.io.File -import sbt.IO._ +import sbt.io.IO._ class FileExamplesTest extends Specification { From 7f57853f23cb52e3ac3da1d9d2b63944d00694d1 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 20 Aug 2015 00:43:22 -0400 Subject: [PATCH 05/50] move modules around. --- {util/collection => internal/util-collection}/NOTICE | 0 .../util-collection}/src/main/scala/sbt/AList.scala | 0 .../util-collection}/src/main/scala/sbt/Attributes.scala | 0 .../util-collection}/src/main/scala/sbt/Classes.scala | 0 .../util-collection}/src/main/scala/sbt/Dag.scala | 0 .../util-collection}/src/main/scala/sbt/HList.scala | 0 .../util-collection}/src/main/scala/sbt/IDSet.scala | 0 .../util-collection}/src/main/scala/sbt/INode.scala | 0 .../util-collection}/src/main/scala/sbt/KList.scala | 0 .../util-collection}/src/main/scala/sbt/PMap.scala | 0 .../util-collection}/src/main/scala/sbt/Param.scala | 0 .../util-collection}/src/main/scala/sbt/Positions.scala | 0 .../util-collection}/src/main/scala/sbt/Settings.scala | 0 .../util-collection}/src/main/scala/sbt/Show.scala | 0 .../util-collection}/src/main/scala/sbt/ShowLines.scala | 0 .../util-collection}/src/main/scala/sbt/Signal.scala | 0 .../util-collection}/src/main/scala/sbt/TypeFunctions.scala | 0 .../util-collection}/src/main/scala/sbt/Types.scala | 0 .../util-collection}/src/main/scala/sbt/Util.scala | 0 .../util-collection}/src/test/scala/DagSpecification.scala | 0 .../util-collection}/src/test/scala/KeyTest.scala | 0 .../util-collection}/src/test/scala/LiteralTest.scala | 0 .../util-collection}/src/test/scala/PMapTest.scala | 0 .../util-collection}/src/test/scala/SettingsExample.scala | 0 .../util-collection}/src/test/scala/SettingsTest.scala | 0 {util/complete => internal/util-complete}/NOTICE | 0 .../util-complete}/src/main/scala/sbt/LineReader.scala | 0 .../util-complete}/src/main/scala/sbt/complete/Completions.scala | 0 .../util-complete}/src/main/scala/sbt/complete/EditDistance.scala | 0 .../src/main/scala/sbt/complete/ExampleSource.scala | 0 .../util-complete}/src/main/scala/sbt/complete/History.scala | 0 .../src/main/scala/sbt/complete/HistoryCommands.scala | 0 .../src/main/scala/sbt/complete/JLineCompletion.scala | 0 .../util-complete}/src/main/scala/sbt/complete/Parser.scala | 0 .../util-complete}/src/main/scala/sbt/complete/Parsers.scala | 0 .../util-complete}/src/main/scala/sbt/complete/ProcessError.scala | 0 .../src/main/scala/sbt/complete/TokenCompletions.scala | 0 .../util-complete}/src/main/scala/sbt/complete/TypeString.scala | 0 .../util-complete}/src/main/scala/sbt/complete/UpperBound.scala | 0 .../util-complete}/src/test/scala/ParserTest.scala | 0 .../src/test/scala/sbt/complete/FileExamplesTest.scala | 0 .../src/test/scala/sbt/complete/FixedSetExamplesTest.scala | 0 .../src/test/scala/sbt/complete/ParserWithExamplesTest.scala | 0 .../util-logic}/src/main/scala/sbt/logic/Logic.scala | 0 .../util-logic}/src/test/scala/sbt/logic/Test.scala | 0 45 files changed, 0 insertions(+), 0 deletions(-) rename {util/collection => internal/util-collection}/NOTICE (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/AList.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Attributes.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Classes.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Dag.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/HList.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/IDSet.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/INode.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/KList.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/PMap.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Param.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Positions.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Settings.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Show.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/ShowLines.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Signal.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/TypeFunctions.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Types.scala (100%) rename {util/collection => internal/util-collection}/src/main/scala/sbt/Util.scala (100%) rename {util/collection => internal/util-collection}/src/test/scala/DagSpecification.scala (100%) rename {util/collection => internal/util-collection}/src/test/scala/KeyTest.scala (100%) rename {util/collection => internal/util-collection}/src/test/scala/LiteralTest.scala (100%) rename {util/collection => internal/util-collection}/src/test/scala/PMapTest.scala (100%) rename {util/collection => internal/util-collection}/src/test/scala/SettingsExample.scala (100%) rename {util/collection => internal/util-collection}/src/test/scala/SettingsTest.scala (100%) rename {util/complete => internal/util-complete}/NOTICE (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/LineReader.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/Completions.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/EditDistance.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/ExampleSource.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/History.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/HistoryCommands.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/JLineCompletion.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/Parser.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/Parsers.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/ProcessError.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/TokenCompletions.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/TypeString.scala (100%) rename {util/complete => internal/util-complete}/src/main/scala/sbt/complete/UpperBound.scala (100%) rename {util/complete => internal/util-complete}/src/test/scala/ParserTest.scala (100%) rename {util/complete => internal/util-complete}/src/test/scala/sbt/complete/FileExamplesTest.scala (100%) rename {util/complete => internal/util-complete}/src/test/scala/sbt/complete/FixedSetExamplesTest.scala (100%) rename {util/complete => internal/util-complete}/src/test/scala/sbt/complete/ParserWithExamplesTest.scala (100%) rename {util/logic => internal/util-logic}/src/main/scala/sbt/logic/Logic.scala (100%) rename {util/logic => internal/util-logic}/src/test/scala/sbt/logic/Test.scala (100%) diff --git a/util/collection/NOTICE b/internal/util-collection/NOTICE similarity index 100% rename from util/collection/NOTICE rename to internal/util-collection/NOTICE diff --git a/util/collection/src/main/scala/sbt/AList.scala b/internal/util-collection/src/main/scala/sbt/AList.scala similarity index 100% rename from util/collection/src/main/scala/sbt/AList.scala rename to internal/util-collection/src/main/scala/sbt/AList.scala diff --git a/util/collection/src/main/scala/sbt/Attributes.scala b/internal/util-collection/src/main/scala/sbt/Attributes.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Attributes.scala rename to internal/util-collection/src/main/scala/sbt/Attributes.scala diff --git a/util/collection/src/main/scala/sbt/Classes.scala b/internal/util-collection/src/main/scala/sbt/Classes.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Classes.scala rename to internal/util-collection/src/main/scala/sbt/Classes.scala diff --git a/util/collection/src/main/scala/sbt/Dag.scala b/internal/util-collection/src/main/scala/sbt/Dag.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Dag.scala rename to internal/util-collection/src/main/scala/sbt/Dag.scala diff --git a/util/collection/src/main/scala/sbt/HList.scala b/internal/util-collection/src/main/scala/sbt/HList.scala similarity index 100% rename from util/collection/src/main/scala/sbt/HList.scala rename to internal/util-collection/src/main/scala/sbt/HList.scala diff --git a/util/collection/src/main/scala/sbt/IDSet.scala b/internal/util-collection/src/main/scala/sbt/IDSet.scala similarity index 100% rename from util/collection/src/main/scala/sbt/IDSet.scala rename to internal/util-collection/src/main/scala/sbt/IDSet.scala diff --git a/util/collection/src/main/scala/sbt/INode.scala b/internal/util-collection/src/main/scala/sbt/INode.scala similarity index 100% rename from util/collection/src/main/scala/sbt/INode.scala rename to internal/util-collection/src/main/scala/sbt/INode.scala diff --git a/util/collection/src/main/scala/sbt/KList.scala b/internal/util-collection/src/main/scala/sbt/KList.scala similarity index 100% rename from util/collection/src/main/scala/sbt/KList.scala rename to internal/util-collection/src/main/scala/sbt/KList.scala diff --git a/util/collection/src/main/scala/sbt/PMap.scala b/internal/util-collection/src/main/scala/sbt/PMap.scala similarity index 100% rename from util/collection/src/main/scala/sbt/PMap.scala rename to internal/util-collection/src/main/scala/sbt/PMap.scala diff --git a/util/collection/src/main/scala/sbt/Param.scala b/internal/util-collection/src/main/scala/sbt/Param.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Param.scala rename to internal/util-collection/src/main/scala/sbt/Param.scala diff --git a/util/collection/src/main/scala/sbt/Positions.scala b/internal/util-collection/src/main/scala/sbt/Positions.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Positions.scala rename to internal/util-collection/src/main/scala/sbt/Positions.scala diff --git a/util/collection/src/main/scala/sbt/Settings.scala b/internal/util-collection/src/main/scala/sbt/Settings.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Settings.scala rename to internal/util-collection/src/main/scala/sbt/Settings.scala diff --git a/util/collection/src/main/scala/sbt/Show.scala b/internal/util-collection/src/main/scala/sbt/Show.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Show.scala rename to internal/util-collection/src/main/scala/sbt/Show.scala diff --git a/util/collection/src/main/scala/sbt/ShowLines.scala b/internal/util-collection/src/main/scala/sbt/ShowLines.scala similarity index 100% rename from util/collection/src/main/scala/sbt/ShowLines.scala rename to internal/util-collection/src/main/scala/sbt/ShowLines.scala diff --git a/util/collection/src/main/scala/sbt/Signal.scala b/internal/util-collection/src/main/scala/sbt/Signal.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Signal.scala rename to internal/util-collection/src/main/scala/sbt/Signal.scala diff --git a/util/collection/src/main/scala/sbt/TypeFunctions.scala b/internal/util-collection/src/main/scala/sbt/TypeFunctions.scala similarity index 100% rename from util/collection/src/main/scala/sbt/TypeFunctions.scala rename to internal/util-collection/src/main/scala/sbt/TypeFunctions.scala diff --git a/util/collection/src/main/scala/sbt/Types.scala b/internal/util-collection/src/main/scala/sbt/Types.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Types.scala rename to internal/util-collection/src/main/scala/sbt/Types.scala diff --git a/util/collection/src/main/scala/sbt/Util.scala b/internal/util-collection/src/main/scala/sbt/Util.scala similarity index 100% rename from util/collection/src/main/scala/sbt/Util.scala rename to internal/util-collection/src/main/scala/sbt/Util.scala diff --git a/util/collection/src/test/scala/DagSpecification.scala b/internal/util-collection/src/test/scala/DagSpecification.scala similarity index 100% rename from util/collection/src/test/scala/DagSpecification.scala rename to internal/util-collection/src/test/scala/DagSpecification.scala diff --git a/util/collection/src/test/scala/KeyTest.scala b/internal/util-collection/src/test/scala/KeyTest.scala similarity index 100% rename from util/collection/src/test/scala/KeyTest.scala rename to internal/util-collection/src/test/scala/KeyTest.scala diff --git a/util/collection/src/test/scala/LiteralTest.scala b/internal/util-collection/src/test/scala/LiteralTest.scala similarity index 100% rename from util/collection/src/test/scala/LiteralTest.scala rename to internal/util-collection/src/test/scala/LiteralTest.scala diff --git a/util/collection/src/test/scala/PMapTest.scala b/internal/util-collection/src/test/scala/PMapTest.scala similarity index 100% rename from util/collection/src/test/scala/PMapTest.scala rename to internal/util-collection/src/test/scala/PMapTest.scala diff --git a/util/collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala similarity index 100% rename from util/collection/src/test/scala/SettingsExample.scala rename to internal/util-collection/src/test/scala/SettingsExample.scala diff --git a/util/collection/src/test/scala/SettingsTest.scala b/internal/util-collection/src/test/scala/SettingsTest.scala similarity index 100% rename from util/collection/src/test/scala/SettingsTest.scala rename to internal/util-collection/src/test/scala/SettingsTest.scala diff --git a/util/complete/NOTICE b/internal/util-complete/NOTICE similarity index 100% rename from util/complete/NOTICE rename to internal/util-complete/NOTICE diff --git a/util/complete/src/main/scala/sbt/LineReader.scala b/internal/util-complete/src/main/scala/sbt/LineReader.scala similarity index 100% rename from util/complete/src/main/scala/sbt/LineReader.scala rename to internal/util-complete/src/main/scala/sbt/LineReader.scala diff --git a/util/complete/src/main/scala/sbt/complete/Completions.scala b/internal/util-complete/src/main/scala/sbt/complete/Completions.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/Completions.scala rename to internal/util-complete/src/main/scala/sbt/complete/Completions.scala diff --git a/util/complete/src/main/scala/sbt/complete/EditDistance.scala b/internal/util-complete/src/main/scala/sbt/complete/EditDistance.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/EditDistance.scala rename to internal/util-complete/src/main/scala/sbt/complete/EditDistance.scala diff --git a/util/complete/src/main/scala/sbt/complete/ExampleSource.scala b/internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/ExampleSource.scala rename to internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala diff --git a/util/complete/src/main/scala/sbt/complete/History.scala b/internal/util-complete/src/main/scala/sbt/complete/History.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/History.scala rename to internal/util-complete/src/main/scala/sbt/complete/History.scala diff --git a/util/complete/src/main/scala/sbt/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/HistoryCommands.scala rename to internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala diff --git a/util/complete/src/main/scala/sbt/complete/JLineCompletion.scala b/internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/JLineCompletion.scala rename to internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala diff --git a/util/complete/src/main/scala/sbt/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/complete/Parser.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/Parser.scala rename to internal/util-complete/src/main/scala/sbt/complete/Parser.scala diff --git a/util/complete/src/main/scala/sbt/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/complete/Parsers.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/Parsers.scala rename to internal/util-complete/src/main/scala/sbt/complete/Parsers.scala diff --git a/util/complete/src/main/scala/sbt/complete/ProcessError.scala b/internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/ProcessError.scala rename to internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala diff --git a/util/complete/src/main/scala/sbt/complete/TokenCompletions.scala b/internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/TokenCompletions.scala rename to internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala diff --git a/util/complete/src/main/scala/sbt/complete/TypeString.scala b/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/TypeString.scala rename to internal/util-complete/src/main/scala/sbt/complete/TypeString.scala diff --git a/util/complete/src/main/scala/sbt/complete/UpperBound.scala b/internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala similarity index 100% rename from util/complete/src/main/scala/sbt/complete/UpperBound.scala rename to internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala diff --git a/util/complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala similarity index 100% rename from util/complete/src/test/scala/ParserTest.scala rename to internal/util-complete/src/test/scala/ParserTest.scala diff --git a/util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala similarity index 100% rename from util/complete/src/test/scala/sbt/complete/FileExamplesTest.scala rename to internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala diff --git a/util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala similarity index 100% rename from util/complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala rename to internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala diff --git a/util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala similarity index 100% rename from util/complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala rename to internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala diff --git a/util/logic/src/main/scala/sbt/logic/Logic.scala b/internal/util-logic/src/main/scala/sbt/logic/Logic.scala similarity index 100% rename from util/logic/src/main/scala/sbt/logic/Logic.scala rename to internal/util-logic/src/main/scala/sbt/logic/Logic.scala diff --git a/util/logic/src/test/scala/sbt/logic/Test.scala b/internal/util-logic/src/test/scala/sbt/logic/Test.scala similarity index 100% rename from util/logic/src/test/scala/sbt/logic/Test.scala rename to internal/util-logic/src/test/scala/sbt/logic/Test.scala From c0f55127db56f4c2b22fb0c6584354314cf5a1f2 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 31 Aug 2015 10:55:02 +0200 Subject: [PATCH 06/50] Add sbt-houserules, formatting. --- .../src/test/scala/SettingsExample.scala | 3 +-- .../src/test/scala/SettingsTest.scala | 9 +++------ .../scala/sbt/complete/HistoryCommands.scala | 3 +-- .../src/main/scala/sbt/complete/TypeString.scala | 3 +-- .../sbt/complete/ParserWithExamplesTest.scala | 16 ++++++---------- .../src/main/scala/sbt/logic/Logic.scala | 3 +-- .../src/test/scala/sbt/logic/Test.scala | 3 +-- 7 files changed, 14 insertions(+), 26 deletions(-) diff --git a/internal/util-collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala index b48bb27fc..3a6bc3853 100644 --- a/internal/util-collection/src/test/scala/SettingsExample.scala +++ b/internal/util-collection/src/test/scala/SettingsExample.scala @@ -49,8 +49,7 @@ object SettingsUsage { val mySettings: Seq[Setting[_]] = Seq( setting(a3, value(3)), setting(b4, map(a4)(_ * 3)), - update(a5)(_ + 1) - ) + update(a5)(_ + 1)) // "compiles" and applies the settings. // This can be split into multiple steps to access intermediate results if desired. diff --git a/internal/util-collection/src/test/scala/SettingsTest.scala b/internal/util-collection/src/test/scala/SettingsTest.scala index d97b1056a..ab92332db 100644 --- a/internal/util-collection/src/test/scala/SettingsTest.scala +++ b/internal/util-collection/src/test/scala/SettingsTest.scala @@ -55,8 +55,7 @@ object SettingsTest extends Properties("settings") { List(scoped0, scoped1) <- chk :: scopedKeys sliding 2 nextInit = if (scoped0 == chk) chk else (scoped0 zipWith chk) { (p, _) => p + 1 } - } yield derive(setting(scoped1, nextInit)) - ).toSeq + } yield derive(setting(scoped1, nextInit))).toSeq { // Note: This causes a cycle refernec error, quite frequently. @@ -96,8 +95,7 @@ object SettingsTest extends Properties("settings") { setting(b, value(6)), derive(setting(b, a)), setting(a, value(5)), - setting(b, value(8)) - ) + setting(b, value(8))) val ev = evaluate(settings) checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev) } @@ -106,8 +104,7 @@ object SettingsTest extends Properties("settings") { setting(a, value(3)), setting(b, value(6)), derive(setting(b, a)), - setting(a, value(5)) - ) + setting(a, value(5))) val ev = evaluate(settings) checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev) } diff --git a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala index c82ecfa3d..ff58fe9d2 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala @@ -34,8 +34,7 @@ object HistoryCommands { Nth -> ("Execute the command with index n, as shown by the " + ListFull + " command"), Previous -> "Execute the nth command before this one", StartsWithString -> "Execute the most recent command starting with 'string'", - ContainsString -> "Execute the most recent command containing 'string'" - ) + ContainsString -> "Execute the most recent command containing 'string'") def helpString = "History commands:\n " + (descriptions.map { case (c, d) => c + " " + d }).mkString("\n ") def printHelp(): Unit = println(helpString) diff --git a/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala b/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala index 6bf89ac05..bd5f84f43 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala @@ -61,8 +61,7 @@ private[sbt] object TypeString { val TypeMap = Map( "java.io.File" -> "File", "java.net.URL" -> "URL", - "java.net.URI" -> "URI" - ) + "java.net.URI" -> "URI") /** * A Parser that extracts basic structure from the string representation of a type from Manifest.toString. diff --git a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala index dff68803c..3bcc55dd2 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -17,8 +17,7 @@ class ParserWithExamplesTest extends Specification { "use the delegate parser to remove invalid examples" in new parserWithValidExamples { val validCompletions = Completions(Set( suggestion("blue"), - suggestion("red") - )) + suggestion("red"))) parserWithExamples.completions(0) shouldEqual validCompletions } } @@ -26,8 +25,7 @@ class ParserWithExamplesTest extends Specification { "listing valid completions in a derived parser" should { "produce only valid examples that start with the character of the derivation" in new parserWithValidExamples { val derivedCompletions = Completions(Set( - suggestion("lue") - )) + suggestion("lue"))) parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions } } @@ -43,8 +41,7 @@ class ParserWithExamplesTest extends Specification { "produce only examples that start with the character of the derivation" in new parserWithAllExamples { val derivedCompletions = Completions(Set( suggestion("lue"), - suggestion("lock") - )) + suggestion("lock"))) parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions } } @@ -56,8 +53,8 @@ class ParserWithExamplesTest extends Specification { class parserWithAllExamples extends parser(removeInvalidExamples = false) case class parser(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), - maxNumberOfExamples: Int = 25, - removeInvalidExamples: Boolean) extends Scope { + maxNumberOfExamples: Int = 25, + removeInvalidExamples: Boolean) extends Scope { import DefaultParsers._ @@ -66,8 +63,7 @@ class ParserWithExamplesTest extends Specification { colorParser, FixedSetExamples(examples), maxNumberOfExamples, - removeInvalidExamples - ) + removeInvalidExamples) } case class GrowableSourceOfExamples() extends Iterable[String] { diff --git a/internal/util-logic/src/main/scala/sbt/logic/Logic.scala b/internal/util-logic/src/main/scala/sbt/logic/Logic.scala index 856394251..0fbbe3f98 100644 --- a/internal/util-logic/src/main/scala/sbt/logic/Logic.scala +++ b/internal/util-logic/src/main/scala/sbt/logic/Logic.scala @@ -103,8 +103,7 @@ object Logic { checkAcyclic(clauses) problem.toLeft( - reduce0(clauses, initialFacts, Matched.empty) - ) + reduce0(clauses, initialFacts, Matched.empty)) } /** diff --git a/internal/util-logic/src/test/scala/sbt/logic/Test.scala b/internal/util-logic/src/test/scala/sbt/logic/Test.scala index f62a9e767..da1f0b706 100644 --- a/internal/util-logic/src/test/scala/sbt/logic/Test.scala +++ b/internal/util-logic/src/test/scala/sbt/logic/Test.scala @@ -20,8 +20,7 @@ object LogicTest extends Properties("Logic") { case Right(res) => false case Left(err: Logic.CyclicNegation) => true case Left(err) => sys.error(s"Expected cyclic error, got: $err") - } - ) + }) def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match { case Left(err) => false From 6c7e69497f0d4fa38bd0f21e6b1ac26919feedcf Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 31 Aug 2015 15:25:10 +0200 Subject: [PATCH 07/50] Add recommended compiler flags, fix most of the warnings --- internal/util-collection/src/main/scala/sbt/Dag.scala | 1 + .../util-collection/src/main/scala/sbt/IDSet.scala | 2 +- .../util-collection/src/main/scala/sbt/INode.scala | 3 ++- .../util-collection/src/main/scala/sbt/PMap.scala | 2 +- .../util-collection/src/main/scala/sbt/Settings.scala | 10 +++++----- .../util-collection/src/main/scala/sbt/Signal.scala | 5 +++-- .../util-complete/src/main/scala/sbt/LineReader.scala | 3 ++- .../src/main/scala/sbt/complete/History.scala | 11 +++++------ .../src/main/scala/sbt/complete/HistoryCommands.scala | 2 +- .../src/main/scala/sbt/complete/JLineCompletion.scala | 2 +- .../src/main/scala/sbt/complete/Parser.scala | 8 ++++---- .../src/main/scala/sbt/complete/Parsers.scala | 4 ++-- .../src/main/scala/sbt/complete/ProcessError.scala | 4 ++-- .../main/scala/sbt/complete/TokenCompletions.scala | 6 +++--- .../src/main/scala/sbt/complete/UpperBound.scala | 4 ++-- .../util-complete/src/test/scala/ParserTest.scala | 8 ++++---- .../util-logic/src/test/scala/sbt/logic/Test.scala | 5 ++++- 17 files changed, 43 insertions(+), 37 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/Dag.scala b/internal/util-collection/src/main/scala/sbt/Dag.scala index 118cd0dff..3eb3d8ccb 100644 --- a/internal/util-collection/src/main/scala/sbt/Dag.scala +++ b/internal/util-collection/src/main/scala/sbt/Dag.scala @@ -26,6 +26,7 @@ object Dag { discovered(node) = true; try { visitAll(dependencies(node)); } catch { case c: Cyclic => throw node :: c } finished += node + () } else if (!finished(node)) throw new Cyclic(node) } diff --git a/internal/util-collection/src/main/scala/sbt/IDSet.scala b/internal/util-collection/src/main/scala/sbt/IDSet.scala index 4f5245a26..7c21c48a7 100644 --- a/internal/util-collection/src/main/scala/sbt/IDSet.scala +++ b/internal/util-collection/src/main/scala/sbt/IDSet.scala @@ -33,7 +33,7 @@ object IDSet { def apply(t: T) = contains(t) def contains(t: T) = backing.containsKey(t) def foreach(f: T => Unit) = all foreach f - def +=(t: T) = backing.put(t, Dummy) + def +=(t: T) = { backing.put(t, Dummy); () } def ++=(t: Iterable[T]) = t foreach += def -=(t: T) = if (backing.remove(t) eq null) false else true def all = collection.JavaConversions.collectionAsScalaIterable(backing.keySet) diff --git a/internal/util-collection/src/main/scala/sbt/INode.scala b/internal/util-collection/src/main/scala/sbt/INode.scala index ce39fadad..e55ee9683 100644 --- a/internal/util-collection/src/main/scala/sbt/INode.scala +++ b/internal/util-collection/src/main/scala/sbt/INode.scala @@ -79,7 +79,7 @@ abstract class EvaluateSettings[Scope] { workComplete() } - private[this] def startWork(): Unit = running.incrementAndGet() + private[this] def startWork(): Unit = { running.incrementAndGet(); () } private[this] def workComplete(): Unit = if (running.decrementAndGet() == 0) complete.put(None) @@ -154,6 +154,7 @@ abstract class EvaluateSettings[Scope] { case Evaluated => submitCallComplete(by, value) case _ => calledBy += by } + () } protected def dependsOn: Seq[INode[_]] protected def evaluate0(): Unit diff --git a/internal/util-collection/src/main/scala/sbt/PMap.scala b/internal/util-collection/src/main/scala/sbt/PMap.scala index cf0454fd9..979e776fa 100644 --- a/internal/util-collection/src/main/scala/sbt/PMap.scala +++ b/internal/util-collection/src/main/scala/sbt/PMap.scala @@ -15,7 +15,7 @@ trait RMap[K[_], V[_]] { def values: Iterable[V[_]] def isEmpty: Boolean - final case class TPair[T](key: K[T], value: V[T]) + sealed case class TPair[T](key: K[T], value: V[T]) } trait IMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { diff --git a/internal/util-collection/src/main/scala/sbt/Settings.scala b/internal/util-collection/src/main/scala/sbt/Settings.scala index eb4227d09..018d5dfc0 100644 --- a/internal/util-collection/src/main/scala/sbt/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/Settings.scala @@ -19,7 +19,7 @@ sealed trait Settings[Scope] { private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope] { def scopes: Set[Scope] = data.keySet def keys(scope: Scope) = data(scope).keys.toSet - def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] = data.flatMap { case (scope, map) => map.keys.map(k => f(scope, k)) } toSeq + def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] = data.flatMap { case (scope, map) => map.keys.map(k => f(scope, k)) }.toSeq def get[T](scope: Scope, key: AttributeKey[T]): Option[T] = delegates(scope).toStream.flatMap(sc => getDirect(sc, key)).headOption @@ -42,7 +42,7 @@ trait Init[Scope] { /** The Show instance used when a detailed String needs to be generated. It is typically used when no context is available.*/ def showFullKey: Show[ScopedKey[_]] - final case class ScopedKey[T](scope: Scope, key: AttributeKey[T]) extends KeyedInitialize[T] { + sealed case class ScopedKey[T](scope: Scope, key: AttributeKey[T]) extends KeyedInitialize[T] { def scopedKey = this } @@ -154,9 +154,9 @@ trait Init[Scope] { def compile(sMap: ScopedMap): CompiledMap = sMap.toTypedSeq.map { case sMap.TPair(k, ss) => - val deps = ss flatMap { _.dependencies } toSet; + val deps = ss.flatMap(_.dependencies).toSet (k, new Compiled(k, deps, ss)) - } toMap; + }.toMap def grouped(init: Seq[Setting[_]]): ScopedMap = ((IMap.empty: ScopedMap) /: init)((m, s) => add(m, s)) @@ -445,7 +445,7 @@ trait Init[Scope] { def join: Initialize[Seq[T]] = uniform(s)(idFun) } def join[T](inits: Seq[Initialize[T]]): Initialize[Seq[T]] = uniform(inits)(idFun) - def joinAny[M[_]](inits: Seq[Initialize[M[T]] forSome { type T }]): Initialize[Seq[M[_]]] = + def joinAny[M[_], T](inits: Seq[Initialize[M[T]]]): Initialize[Seq[M[_]]] = join(inits.asInstanceOf[Seq[Initialize[M[Any]]]]).asInstanceOf[Initialize[Seq[M[T] forSome { type T }]]] } object SettingsDefinition { diff --git a/internal/util-collection/src/main/scala/sbt/Signal.scala b/internal/util-collection/src/main/scala/sbt/Signal.scala index e8c9e7e6c..5aa5fc86e 100644 --- a/internal/util-collection/src/main/scala/sbt/Signal.scala +++ b/internal/util-collection/src/main/scala/sbt/Signal.scala @@ -37,6 +37,7 @@ object Signals { object unregisterNewHandler extends Registration { override def remove(): Unit = { Signal.handle(intSignal, oldHandler) + () } } unregisterNewHandler @@ -80,6 +81,6 @@ private final class Signals0 { try Right(action()) catch { case e: LinkageError => Left(e) } - finally Signal.handle(intSignal, oldHandler) + finally { Signal.handle(intSignal, oldHandler); () } } -} \ No newline at end of file +} diff --git a/internal/util-complete/src/main/scala/sbt/LineReader.scala b/internal/util-complete/src/main/scala/sbt/LineReader.scala index b85190f92..41679ab6e 100644 --- a/internal/util-complete/src/main/scala/sbt/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/LineReader.scala @@ -65,7 +65,7 @@ private object JLine { // translate explicit class names to type in order to support // older Scala, since it shaded classes but not the system property - private[sbt] def fixTerminalProperty() { + private[sbt] def fixTerminalProperty(): Unit = { val newValue = System.getProperty(TerminalProperty) match { case "jline.UnixTerminal" => "unix" case null if System.getProperty("sbt.cygwin") != null => "unix" @@ -75,6 +75,7 @@ private object JLine { case x => x } if (newValue != null) System.setProperty(TerminalProperty, newValue) + () } // When calling this, ensure that enableEcho has been or will be called. diff --git a/internal/util-complete/src/main/scala/sbt/complete/History.scala b/internal/util-complete/src/main/scala/sbt/complete/History.scala index 26d0a27c6..614244b9e 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/History.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/History.scala @@ -13,7 +13,7 @@ final class History private (val lines: IndexedSeq[String], val path: Option[Fil def all: Seq[String] = lines def size = lines.length def !! : Option[String] = !-(1) - def apply(i: Int): Option[String] = if (0 <= i && i < size) Some(lines(i)) else { sys.error("Invalid history index: " + i); None } + def apply(i: Int): Option[String] = if (0 <= i && i < size) Some(lines(i)) else { sys.error("Invalid history index: " + i) } def !(i: Int): Option[String] = apply(i) def !(s: String): Option[String] = @@ -26,14 +26,13 @@ final class History private (val lines: IndexedSeq[String], val path: Option[Fil def !?(s: String): Option[String] = nonEmpty(s) { reversed.drop(1).find(_.contains(s)) } private def nonEmpty[T](s: String)(act: => Option[T]): Option[T] = - if (s.isEmpty) { + if (s.isEmpty) sys.error("No action specified to history command") - None - } else + else act def list(historySize: Int, show: Int): Seq[String] = - lines.toList.drop((lines.size - historySize) max 0).zipWithIndex.map { case (line, number) => " " + number + " " + line }.takeRight(show max 1) + lines.toList.drop(scala.math.max(0, lines.size - historySize)).zipWithIndex.map { case (line, number) => " " + number + " " + line }.takeRight(show max 1) } object History { @@ -42,4 +41,4 @@ object History { def number(s: String): Option[Int] = try { Some(s.toInt) } catch { case e: NumberFormatException => None } -} \ No newline at end of file +} diff --git a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala index ff58fe9d2..b66d3272e 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala @@ -45,7 +45,7 @@ object HistoryCommands { val MaxLines = 500 lazy val num = token(NatBasic, "") - lazy val last = Last ^^^ { execute(_ !!) } + lazy val last = Last ^^^ { execute(_.!!) } lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => (h: History) => { printHistory(h, MaxLines, show); Some(Nil) } } diff --git a/internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala b/internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala index fed89541f..2445fd111 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala @@ -149,7 +149,7 @@ object JLineCompletion { def commonPrefix(s: Seq[String]): String = if (s.isEmpty) "" else s reduceLeft commonPrefix def commonPrefix(a: String, b: String): String = { - val len = a.length min b.length + val len = scala.math.min(a.length, b.length) @tailrec def loop(i: Int): Int = if (i >= len) len else if (a(i) != b(i)) i else loop(i + 1) a.substring(0, loop(0)) } diff --git a/internal/util-complete/src/main/scala/sbt/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/complete/Parser.scala index 892119f6c..7ae7ea0fd 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/Parser.scala @@ -300,9 +300,9 @@ trait ParserMain { def !!!(msg: String): Parser[A] = onFailure(a, msg) def failOnException: Parser[A] = trapAndFail(a) - def unary_- = not(a) + def unary_- = not(a, "Unexpected: " + a) def &(o: Parser[_]) = and(a, o) - def -(o: Parser[_]) = sub(a, o) + def -(o: Parser[_]) = and(a, not(o, "Unexpected: " + o)) def examples(s: String*): Parser[A] = examples(s.toSet) def examples(s: Set[String], check: Boolean = false): Parser[A] = examples(new FixedSetExamples(s), s.size, check) def examples(s: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] = Parser.examples(a, s, maxNumberOfExamples, removeInvalidExamples) @@ -366,7 +366,7 @@ trait ParserMain { def result = None def resultEmpty = mkFailure("Expected '" + ch + "'") def derive(c: Char) = if (c == ch) success(ch) else new Invalid(resultEmpty) - def completions(level: Int) = Completions.single(Completion.suggestStrict(ch.toString)) + def completions(level: Int) = Completions.single(Completion.suggestion(ch.toString)) override def toString = "'" + ch + "'" } /** Presents a literal String `s` as a Parser that only parses that exact text and provides it as the result.*/ @@ -812,7 +812,7 @@ private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], m case None => repeatDerive(c, accumulatedReverse) } - def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = repeat(Some(repeated derive c), repeated, (min - 1) max 0, max.decrement, accRev) + def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = repeat(Some(repeated derive c), repeated, scala.math.max(0, min - 1), max.decrement, accRev) def completions(level: Int) = { diff --git a/internal/util-complete/src/main/scala/sbt/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/complete/Parsers.scala index 3183929e8..af5849870 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/Parsers.scala @@ -11,7 +11,7 @@ import java.lang.Character.{ getType, MATH_SYMBOL, OTHER_SYMBOL, DASH_PUNCTUATIO /** Provides standard implementations of commonly useful [[Parser]]s. */ trait Parsers { /** Matches the end of input, providing no useful result on success. */ - lazy val EOF = not(any) + lazy val EOF = not(any, "Expected EOF") /** Parses any single character and provides that character as the result. */ lazy val any: Parser[Char] = charClass(_ => true, "any character") @@ -265,4 +265,4 @@ object DefaultParsers extends Parsers with ParserMain { /** Returns `true` if `s` parses successfully according to [[ID]].*/ def validID(s: String): Boolean = matches(ID, s) -} \ No newline at end of file +} diff --git a/internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala b/internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala index 7e6c9794e..d85e523c9 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala @@ -25,5 +25,5 @@ object ProcessError { s.substring(i + 1) loop(s.length - 1) } - def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString; -} \ No newline at end of file + def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString "" +} diff --git a/internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala b/internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala index 96e70d2f1..1285507cc 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala @@ -1,6 +1,6 @@ package sbt.complete -import Completion.{ displayStrict, token => ctoken, tokenDisplay } +import Completion.{ token => ctoken, tokenDisplay } sealed trait TokenCompletions { def hideWhen(f: Int => Boolean): TokenCompletions @@ -24,7 +24,7 @@ object TokenCompletions { val default: TokenCompletions = mapDelegateCompletions((seen, level, c) => ctoken(seen, c.append)) def displayOnly(msg: String): TokenCompletions = new Fixed { - def completions(seen: String, level: Int) = Completions.single(displayStrict(msg)) + def completions(seen: String, level: Int) = Completions.single(Completion.displayOnly(msg)) } def overrideDisplay(msg: String): TokenCompletions = mapDelegateCompletions((seen, level, c) => tokenDisplay(display = msg, append = c.append)) @@ -34,4 +34,4 @@ object TokenCompletions { def mapDelegateCompletions(f: (String, Int, Completion) => Completion): TokenCompletions = new Delegating { def completions(seen: String, level: Int, delegate: Completions) = Completions(delegate.get.map(c => f(seen, level, c))) } -} \ No newline at end of file +} diff --git a/internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala b/internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala index 66a32e1a2..2d954d4c5 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala @@ -38,10 +38,10 @@ final case class Finite(value: Int) extends UpperBound { def >=(min: Int) = value >= min def isOne = value == 1 def isZero = value == 0 - def decrement = Finite((value - 1) max 0) + def decrement = Finite(scala.math.max(0, value - 1)) def isInfinite = false override def toString = value.toString } object UpperBound { implicit def intToFinite(i: Int): Finite = Finite(i) -} \ No newline at end of file +} diff --git a/internal/util-complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala index f02431e53..c70c6207b 100644 --- a/internal/util-complete/src/test/scala/ParserTest.scala +++ b/internal/util-complete/src/test/scala/ParserTest.scala @@ -109,13 +109,13 @@ object ParserTest extends Properties("Completing Parser") { property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) } object ParserExample { - val ws = charClass(_.isWhitespace)+ - val notws = charClass(!_.isWhitespace)+ + val ws = charClass(_.isWhitespace).+ + val notws = charClass(!_.isWhitespace).+ val name = token("test") - val options = (ws ~> token("quick" | "failed" | "new"))* + val options = (ws ~> token("quick" | "failed" | "new")).* val exampleSet = Set("am", "is", "are", "was", "were") - val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false)))* + val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false))).* val t = name ~ options ~ include diff --git a/internal/util-logic/src/test/scala/sbt/logic/Test.scala b/internal/util-logic/src/test/scala/sbt/logic/Test.scala index da1f0b706..f9957414b 100644 --- a/internal/util-logic/src/test/scala/sbt/logic/Test.scala +++ b/internal/util-logic/src/test/scala/sbt/logic/Test.scala @@ -26,7 +26,10 @@ object LogicTest extends Properties("Logic") { case Left(err) => false case Right(res) => val actual = res.provenSet - (actual == expected) || sys.error(s"Expected to prove $expected, but actually proved $actual") + if (actual != expected) + sys.error(s"Expected to prove $expected, but actually proved $actual") + else + true } } From dc29953fe8c70d1fbb5fbf02c40360df1251eef0 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 31 Aug 2015 03:02:44 -0400 Subject: [PATCH 08/50] Add sbt-house-rules and bintray-sbt --- .../src/main/scala/sbt/complete/HistoryCommands.scala | 3 +-- .../util-complete/src/main/scala/sbt/complete/TypeString.scala | 3 +-- internal/util-logic/src/main/scala/sbt/logic/Logic.scala | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala index c82ecfa3d..ff58fe9d2 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala @@ -34,8 +34,7 @@ object HistoryCommands { Nth -> ("Execute the command with index n, as shown by the " + ListFull + " command"), Previous -> "Execute the nth command before this one", StartsWithString -> "Execute the most recent command starting with 'string'", - ContainsString -> "Execute the most recent command containing 'string'" - ) + ContainsString -> "Execute the most recent command containing 'string'") def helpString = "History commands:\n " + (descriptions.map { case (c, d) => c + " " + d }).mkString("\n ") def printHelp(): Unit = println(helpString) diff --git a/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala b/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala index 6bf89ac05..bd5f84f43 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala +++ b/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala @@ -61,8 +61,7 @@ private[sbt] object TypeString { val TypeMap = Map( "java.io.File" -> "File", "java.net.URL" -> "URL", - "java.net.URI" -> "URI" - ) + "java.net.URI" -> "URI") /** * A Parser that extracts basic structure from the string representation of a type from Manifest.toString. diff --git a/internal/util-logic/src/main/scala/sbt/logic/Logic.scala b/internal/util-logic/src/main/scala/sbt/logic/Logic.scala index 856394251..0fbbe3f98 100644 --- a/internal/util-logic/src/main/scala/sbt/logic/Logic.scala +++ b/internal/util-logic/src/main/scala/sbt/logic/Logic.scala @@ -103,8 +103,7 @@ object Logic { checkAcyclic(clauses) problem.toLeft( - reduce0(clauses, initialFacts, Matched.empty) - ) + reduce0(clauses, initialFacts, Matched.empty)) } /** From 7e3ad3631cacf5497a99ce0c730db4041379374a Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 4 Sep 2015 07:25:16 -0400 Subject: [PATCH 09/50] Removes a warning. Ref #4 Exhaustion check was not picking up StaticScopes, which is an object. --- internal/util-collection/src/main/scala/sbt/INode.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/util-collection/src/main/scala/sbt/INode.scala b/internal/util-collection/src/main/scala/sbt/INode.scala index e55ee9683..53f45e9f1 100644 --- a/internal/util-collection/src/main/scala/sbt/INode.scala +++ b/internal/util-collection/src/main/scala/sbt/INode.scala @@ -27,7 +27,6 @@ abstract class EvaluateSettings[Scope] { case k: Keyed[s, T] @unchecked => single(getStatic(k.scopedKey), k.transform) case a: Apply[k, T] @unchecked => new MixedNode[k, T](a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist) case b: Bind[s, T] @unchecked => new BindNode[s, T](transform(b.in), x => transform(b.f(x))) - case init.StaticScopes => strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope] case v: Value[T] @unchecked => constant(v.value) case v: ValidationCapture[T] @unchecked => strictConstant(v.key) case t: TransformCapture => strictConstant(t.f) @@ -35,6 +34,7 @@ abstract class EvaluateSettings[Scope] { case None => constant(() => o.f(None)) case Some(i) => single[s, T](transform(i), x => o.f(Some(x))) } + case x if x == StaticScopes => strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope] } } private[this] lazy val roots: Seq[INode[_]] = compiledSettings flatMap { cs => From 53e719656ff328301506890106fdcd02b99dfa66 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 4 Sep 2015 12:54:09 -0400 Subject: [PATCH 10/50] Move util into sbt.util.internal package --- .../src/main/scala/sbt/{ => util/internal}/AList.scala | 2 +- .../main/scala/sbt/{ => util/internal}/Attributes.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Classes.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Dag.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/HList.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/IDSet.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/INode.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/KList.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/PMap.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Param.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Positions.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Settings.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Show.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/ShowLines.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Signal.scala | 2 +- .../main/scala/sbt/{ => util/internal}/TypeFunctions.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Types.scala | 2 +- .../src/main/scala/sbt/{ => util/internal}/Util.scala | 2 +- .../util-collection/src/test/scala/DagSpecification.scala | 2 +- internal/util-collection/src/test/scala/KeyTest.scala | 2 +- internal/util-collection/src/test/scala/LiteralTest.scala | 2 +- internal/util-collection/src/test/scala/PMapTest.scala | 2 +- .../util-collection/src/test/scala/SettingsExample.scala | 2 +- internal/util-collection/src/test/scala/SettingsTest.scala | 2 +- .../main/scala/sbt/{ => util/internal}/LineReader.scala | 4 ++-- .../sbt/{ => util/internal}/complete/Completions.scala | 3 ++- .../sbt/{ => util/internal}/complete/EditDistance.scala | 3 ++- .../sbt/{ => util/internal}/complete/ExampleSource.scala | 3 ++- .../scala/sbt/{ => util/internal}/complete/History.scala | 2 +- .../sbt/{ => util/internal}/complete/HistoryCommands.scala | 2 +- .../sbt/{ => util/internal}/complete/JLineCompletion.scala | 3 ++- .../scala/sbt/{ => util/internal}/complete/Parser.scala | 7 ++++--- .../scala/sbt/{ => util/internal}/complete/Parsers.scala | 3 ++- .../sbt/{ => util/internal}/complete/ProcessError.scala | 3 ++- .../{ => util/internal}/complete/TokenCompletions.scala | 3 ++- .../sbt/{ => util/internal}/complete/TypeString.scala | 3 ++- .../sbt/{ => util/internal}/complete/UpperBound.scala | 3 ++- internal/util-complete/src/test/scala/ParserTest.scala | 3 ++- .../src/test/scala/sbt/complete/FileExamplesTest.scala | 3 ++- .../src/test/scala/sbt/complete/FixedSetExamplesTest.scala | 3 ++- .../test/scala/sbt/complete/ParserWithExamplesTest.scala | 3 ++- .../main/scala/sbt/{ => util/internal}/logic/Logic.scala | 2 +- internal/util-logic/src/test/scala/sbt/logic/Test.scala | 2 +- 43 files changed, 60 insertions(+), 46 deletions(-) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/AList.scala (99%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Attributes.scala (99%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Classes.scala (97%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Dag.scala (99%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/HList.scala (96%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/IDSet.scala (98%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/INode.scala (99%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/KList.scala (98%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/PMap.scala (99%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Param.scala (95%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Positions.scala (94%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Settings.scala (99%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Show.scala (84%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/ShowLines.scala (92%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Signal.scala (99%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/TypeFunctions.scala (98%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Types.scala (88%) rename internal/util-collection/src/main/scala/sbt/{ => util/internal}/Util.scala (98%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/LineReader.scala (97%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/Completions.scala (99%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/EditDistance.scala (96%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/ExampleSource.scala (98%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/History.scala (98%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/HistoryCommands.scala (98%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/JLineCompletion.scala (99%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/Parser.scala (99%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/Parsers.scala (99%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/ProcessError.scala (95%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/TokenCompletions.scala (97%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/TypeString.scala (98%) rename internal/util-complete/src/main/scala/sbt/{ => util/internal}/complete/UpperBound.scala (97%) rename internal/util-logic/src/main/scala/sbt/{ => util/internal}/logic/Logic.scala (99%) diff --git a/internal/util-collection/src/main/scala/sbt/AList.scala b/internal/util-collection/src/main/scala/sbt/util/internal/AList.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/AList.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/AList.scala index 24368219b..e825b385c 100644 --- a/internal/util-collection/src/main/scala/sbt/AList.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/AList.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal import Classes.Applicative import Types._ diff --git a/internal/util-collection/src/main/scala/sbt/Attributes.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Attributes.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/Attributes.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Attributes.scala index 64f379012..fb4bb8eaa 100644 --- a/internal/util-collection/src/main/scala/sbt/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Attributes.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal import Types._ import scala.reflect.Manifest diff --git a/internal/util-collection/src/main/scala/sbt/Classes.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Classes.scala similarity index 97% rename from internal/util-collection/src/main/scala/sbt/Classes.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Classes.scala index 1db644f96..678eb0651 100644 --- a/internal/util-collection/src/main/scala/sbt/Classes.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Classes.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal object Classes { trait Applicative[M[_]] { diff --git a/internal/util-collection/src/main/scala/sbt/Dag.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Dag.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/Dag.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Dag.scala index 3eb3d8ccb..d41730e88 100644 --- a/internal/util-collection/src/main/scala/sbt/Dag.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Dag.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2009, 2010 David MacIver, Mark Harrah */ -package sbt; +package sbt.util.internal trait Dag[Node <: Dag[Node]] { self: Node => diff --git a/internal/util-collection/src/main/scala/sbt/HList.scala b/internal/util-collection/src/main/scala/sbt/util/internal/HList.scala similarity index 96% rename from internal/util-collection/src/main/scala/sbt/HList.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/HList.scala index 23f5488c6..01cded498 100644 --- a/internal/util-collection/src/main/scala/sbt/HList.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/HList.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal import Types._ diff --git a/internal/util-collection/src/main/scala/sbt/IDSet.scala b/internal/util-collection/src/main/scala/sbt/util/internal/IDSet.scala similarity index 98% rename from internal/util-collection/src/main/scala/sbt/IDSet.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/IDSet.scala index 7c21c48a7..cefe13186 100644 --- a/internal/util-collection/src/main/scala/sbt/IDSet.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/IDSet.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal /** A mutable set interface that uses object identity to test for set membership.*/ trait IDSet[T] { diff --git a/internal/util-collection/src/main/scala/sbt/INode.scala b/internal/util-collection/src/main/scala/sbt/util/internal/INode.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/INode.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/INode.scala index 53f45e9f1..3e2310f2f 100644 --- a/internal/util-collection/src/main/scala/sbt/INode.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/INode.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal import java.lang.Runnable import java.util.concurrent.{ atomic, Executor, LinkedBlockingQueue } diff --git a/internal/util-collection/src/main/scala/sbt/KList.scala b/internal/util-collection/src/main/scala/sbt/util/internal/KList.scala similarity index 98% rename from internal/util-collection/src/main/scala/sbt/KList.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/KList.scala index 0b09ac9b1..1df8c4df5 100644 --- a/internal/util-collection/src/main/scala/sbt/KList.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/KList.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal import Types._ import Classes.Applicative diff --git a/internal/util-collection/src/main/scala/sbt/PMap.scala b/internal/util-collection/src/main/scala/sbt/util/internal/PMap.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/PMap.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/PMap.scala index 979e776fa..f9ac4b0a0 100644 --- a/internal/util-collection/src/main/scala/sbt/PMap.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/PMap.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal import collection.mutable diff --git a/internal/util-collection/src/main/scala/sbt/Param.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Param.scala similarity index 95% rename from internal/util-collection/src/main/scala/sbt/Param.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Param.scala index 19d12798a..16bd17e49 100644 --- a/internal/util-collection/src/main/scala/sbt/Param.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Param.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal import Types._ diff --git a/internal/util-collection/src/main/scala/sbt/Positions.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Positions.scala similarity index 94% rename from internal/util-collection/src/main/scala/sbt/Positions.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Positions.scala index 5d7e1915d..fd64e4538 100755 --- a/internal/util-collection/src/main/scala/sbt/Positions.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Positions.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal sealed trait SourcePosition diff --git a/internal/util-collection/src/main/scala/sbt/Settings.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Settings.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/Settings.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Settings.scala index 018d5dfc0..9e6cdf95e 100644 --- a/internal/util-collection/src/main/scala/sbt/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Settings.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ -package sbt +package sbt.util.internal import Types._ diff --git a/internal/util-collection/src/main/scala/sbt/Show.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Show.scala similarity index 84% rename from internal/util-collection/src/main/scala/sbt/Show.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Show.scala index 1f8e9703b..930431e13 100644 --- a/internal/util-collection/src/main/scala/sbt/Show.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Show.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal trait Show[T] { def apply(t: T): String diff --git a/internal/util-collection/src/main/scala/sbt/ShowLines.scala b/internal/util-collection/src/main/scala/sbt/util/internal/ShowLines.scala similarity index 92% rename from internal/util-collection/src/main/scala/sbt/ShowLines.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/ShowLines.scala index 126b6360e..794f3d5cb 100644 --- a/internal/util-collection/src/main/scala/sbt/ShowLines.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/ShowLines.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal trait ShowLines[A] { def showLines(a: A): Seq[String] diff --git a/internal/util-collection/src/main/scala/sbt/Signal.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Signal.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/Signal.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Signal.scala index 5aa5fc86e..f8e3ed17d 100644 --- a/internal/util-collection/src/main/scala/sbt/Signal.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Signal.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal object Signals { val CONT = "CONT" diff --git a/internal/util-collection/src/main/scala/sbt/TypeFunctions.scala b/internal/util-collection/src/main/scala/sbt/util/internal/TypeFunctions.scala similarity index 98% rename from internal/util-collection/src/main/scala/sbt/TypeFunctions.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/TypeFunctions.scala index 74f0a7d99..ed1669f35 100644 --- a/internal/util-collection/src/main/scala/sbt/TypeFunctions.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/TypeFunctions.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal trait TypeFunctions { type Id[X] = X diff --git a/internal/util-collection/src/main/scala/sbt/Types.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Types.scala similarity index 88% rename from internal/util-collection/src/main/scala/sbt/Types.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Types.scala index 29994f3d1..972f12769 100644 --- a/internal/util-collection/src/main/scala/sbt/Types.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Types.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal object Types extends Types diff --git a/internal/util-collection/src/main/scala/sbt/Util.scala b/internal/util-collection/src/main/scala/sbt/util/internal/Util.scala similarity index 98% rename from internal/util-collection/src/main/scala/sbt/Util.scala rename to internal/util-collection/src/main/scala/sbt/util/internal/Util.scala index befc7b5a9..ba158ae57 100644 --- a/internal/util-collection/src/main/scala/sbt/Util.scala +++ b/internal/util-collection/src/main/scala/sbt/util/internal/Util.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ -package sbt +package sbt.util.internal import java.util.Locale diff --git a/internal/util-collection/src/test/scala/DagSpecification.scala b/internal/util-collection/src/test/scala/DagSpecification.scala index abf9ddf28..47c1b802b 100644 --- a/internal/util-collection/src/test/scala/DagSpecification.scala +++ b/internal/util-collection/src/test/scala/DagSpecification.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008 Mark Harrah */ -package sbt +package sbt.util.internal import org.scalacheck._ import Prop._ diff --git a/internal/util-collection/src/test/scala/KeyTest.scala b/internal/util-collection/src/test/scala/KeyTest.scala index f48e3742a..8bdcd76bf 100644 --- a/internal/util-collection/src/test/scala/KeyTest.scala +++ b/internal/util-collection/src/test/scala/KeyTest.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal import org.scalacheck._ import Prop._ diff --git a/internal/util-collection/src/test/scala/LiteralTest.scala b/internal/util-collection/src/test/scala/LiteralTest.scala index 35ef373ca..32da58a7b 100644 --- a/internal/util-collection/src/test/scala/LiteralTest.scala +++ b/internal/util-collection/src/test/scala/LiteralTest.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal import Types._ diff --git a/internal/util-collection/src/test/scala/PMapTest.scala b/internal/util-collection/src/test/scala/PMapTest.scala index 6a6c558c1..e8aa2c6fa 100644 --- a/internal/util-collection/src/test/scala/PMapTest.scala +++ b/internal/util-collection/src/test/scala/PMapTest.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal import Types._ diff --git a/internal/util-collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala index 3a6bc3853..96a8e01f6 100644 --- a/internal/util-collection/src/test/scala/SettingsExample.scala +++ b/internal/util-collection/src/test/scala/SettingsExample.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal /** Define our settings system */ diff --git a/internal/util-collection/src/test/scala/SettingsTest.scala b/internal/util-collection/src/test/scala/SettingsTest.scala index ab92332db..3321c644b 100644 --- a/internal/util-collection/src/test/scala/SettingsTest.scala +++ b/internal/util-collection/src/test/scala/SettingsTest.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal import org.scalacheck._ import Prop._ diff --git a/internal/util-complete/src/main/scala/sbt/LineReader.scala b/internal/util-complete/src/main/scala/sbt/util/internal/LineReader.scala similarity index 97% rename from internal/util-complete/src/main/scala/sbt/LineReader.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/LineReader.scala index 41679ab6e..c01986a0f 100644 --- a/internal/util-complete/src/main/scala/sbt/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/LineReader.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2009 Mark Harrah */ -package sbt +package sbt.util.internal import jline.console.ConsoleReader import jline.console.history.{ FileHistory, MemoryHistory } @@ -128,7 +128,7 @@ final class FullReader(historyPath: Option[File], complete: Parser[_], val handl protected[this] val reader = { val cr = JLine.createReader(historyPath) - sbt.complete.JLineCompletion.installCustomCompletor(cr, complete) + sbt.util.internal.complete.JLineCompletion.installCustomCompletor(cr, complete) cr } } diff --git a/internal/util-complete/src/main/scala/sbt/complete/Completions.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/Completions.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/complete/Completions.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/Completions.scala index 5237ad26d..b3a562dc7 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/Completions.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/Completions.scala @@ -1,7 +1,8 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.complete +package sbt.util.internal +package complete /** * Represents a set of completions. diff --git a/internal/util-complete/src/main/scala/sbt/complete/EditDistance.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/EditDistance.scala similarity index 96% rename from internal/util-complete/src/main/scala/sbt/complete/EditDistance.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/EditDistance.scala index 95ed0c91f..873d9035c 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/EditDistance.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/EditDistance.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete import java.lang.Character.{ toLowerCase => lower } diff --git a/internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/ExampleSource.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/ExampleSource.scala index ab2d39b4c..932951e34 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/ExampleSource.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/ExampleSource.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete import java.io.File import sbt.io.IO diff --git a/internal/util-complete/src/main/scala/sbt/complete/History.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/History.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/complete/History.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/History.scala index 614244b9e..bd925cd17 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/History.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/History.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal package complete import History.number diff --git a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/HistoryCommands.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/HistoryCommands.scala index b66d3272e..662b914c0 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/HistoryCommands.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt +package sbt.util.internal package complete import java.io.File diff --git a/internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/JLineCompletion.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/JLineCompletion.scala index 2445fd111..c674e21db 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/JLineCompletion.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/JLineCompletion.scala @@ -1,7 +1,8 @@ /* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ -package sbt.complete +package sbt.util.internal +package complete import jline.console.ConsoleReader import jline.console.completer.{ CandidateListCompletionHandler, Completer, CompletionHandler } diff --git a/internal/util-complete/src/main/scala/sbt/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/Parser.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/complete/Parser.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/Parser.scala index 7ae7ea0fd..659f20e7b 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/Parser.scala @@ -1,11 +1,12 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2010, 2011 Mark Harrah */ -package sbt.complete +package sbt.util.internal +package complete import Parser._ -import sbt.Types.{ left, right, some } -import sbt.Util.{ makeList, separate } +import sbt.util.internal.Types.{ left, right, some } +import sbt.util.internal.Util.{ makeList, separate } /** * A String parser that provides semi-automatic tab completion. diff --git a/internal/util-complete/src/main/scala/sbt/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/Parsers.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/complete/Parsers.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/Parsers.scala index af5849870..e0b81884f 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/Parsers.scala @@ -1,7 +1,8 @@ /* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ -package sbt.complete +package sbt.util.internal +package complete import Parser._ import java.io.File diff --git a/internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/ProcessError.scala similarity index 95% rename from internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/ProcessError.scala index d85e523c9..2faa17634 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/ProcessError.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/ProcessError.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete object ProcessError { def apply(command: String, msgs: Seq[String], index: Int): String = diff --git a/internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/TokenCompletions.scala similarity index 97% rename from internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/TokenCompletions.scala index 1285507cc..6d3e5100f 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/TokenCompletions.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/TokenCompletions.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete import Completion.{ token => ctoken, tokenDisplay } diff --git a/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/TypeString.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/complete/TypeString.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/TypeString.scala index bd5f84f43..1ac092023 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/TypeString.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/TypeString.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete import DefaultParsers._ import TypeString._ diff --git a/internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala b/internal/util-complete/src/main/scala/sbt/util/internal/complete/UpperBound.scala similarity index 97% rename from internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala rename to internal/util-complete/src/main/scala/sbt/util/internal/complete/UpperBound.scala index 2d954d4c5..cd42e688b 100644 --- a/internal/util-complete/src/main/scala/sbt/complete/UpperBound.scala +++ b/internal/util-complete/src/main/scala/sbt/util/internal/complete/UpperBound.scala @@ -1,7 +1,8 @@ /* sbt -- Simple Build Tool * Copyright 2008,2010 Mark Harrah */ -package sbt.complete +package sbt.util.internal +package complete sealed trait UpperBound { /** True if and only if the given value meets this bound.*/ diff --git a/internal/util-complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala index c70c6207b..c1bd8c45c 100644 --- a/internal/util-complete/src/test/scala/ParserTest.scala +++ b/internal/util-complete/src/test/scala/ParserTest.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete object JLineTest { import DefaultParsers._ diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index f9cc77038..a0db3d777 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete import org.specs2.mutable.Specification import org.specs2.specification.Scope diff --git a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala index b5aa14250..4323b8e24 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete import org.specs2.mutable.Specification import org.specs2.specification.Scope diff --git a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala index 3bcc55dd2..854569e88 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -1,4 +1,5 @@ -package sbt.complete +package sbt.util.internal +package complete import org.specs2.mutable.Specification import org.specs2.specification.Scope diff --git a/internal/util-logic/src/main/scala/sbt/logic/Logic.scala b/internal/util-logic/src/main/scala/sbt/util/internal/logic/Logic.scala similarity index 99% rename from internal/util-logic/src/main/scala/sbt/logic/Logic.scala rename to internal/util-logic/src/main/scala/sbt/util/internal/logic/Logic.scala index 0fbbe3f98..3e6f8ba8c 100644 --- a/internal/util-logic/src/main/scala/sbt/logic/Logic.scala +++ b/internal/util-logic/src/main/scala/sbt/util/internal/logic/Logic.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package logic import scala.annotation.tailrec diff --git a/internal/util-logic/src/test/scala/sbt/logic/Test.scala b/internal/util-logic/src/test/scala/sbt/logic/Test.scala index f9957414b..f6170f1e3 100644 --- a/internal/util-logic/src/test/scala/sbt/logic/Test.scala +++ b/internal/util-logic/src/test/scala/sbt/logic/Test.scala @@ -1,4 +1,4 @@ -package sbt +package sbt.util.internal package logic import org.scalacheck._ From d93db4a634845885cedff5294f4f85e820f06d04 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 4 Sep 2015 17:40:48 -0400 Subject: [PATCH 11/50] migrate to scalatest 2.2.4 --- .../scala/sbt/complete/FileExamplesTest.scala | 80 ++++++++++--------- .../sbt/complete/FixedSetExamplesTest.scala | 23 +++--- .../sbt/complete/ParserWithExamplesTest.scala | 79 +++++++++--------- 3 files changed, 94 insertions(+), 88 deletions(-) diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index a0db3d777..2f873cf6c 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -1,54 +1,60 @@ package sbt.util.internal package complete -import org.specs2.mutable.Specification -import org.specs2.specification.Scope import java.io.File import sbt.io.IO._ -class FileExamplesTest extends Specification { +class FileExamplesTest extends UnitSpec { - "listing all files in an absolute base directory" should { - "produce the entire base directory's contents" in new directoryStructure { - fileExamples().toList should containTheSameElementsAs(allRelativizedPaths) + "listing all files in an absolute base directory" should + "produce the entire base directory's contents" in { + val _ = new DirectoryStructure { + fileExamples().toList should contain theSameElementsAs (allRelativizedPaths) + } + } + + "listing files with a prefix that matches none" should + "produce an empty list" in { + val _ = new DirectoryStructure(withCompletionPrefix = "z") { + fileExamples().toList shouldBe empty + } + } + + "listing single-character prefixed files" should + "produce matching paths only" in { + val _ = new DirectoryStructure(withCompletionPrefix = "f") { + fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) + } + } + + "listing directory-prefixed files" should + "produce matching paths only" in { + val _ = new DirectoryStructure(withCompletionPrefix = "far") { + fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) + } + } + + it should "produce sub-dir contents only when appending a file separator to the directory" in { + val _ = new DirectoryStructure(withCompletionPrefix = "far" + File.separator) { + fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) } } - "listing files with a prefix that matches none" should { - "produce an empty list" in new directoryStructure(withCompletionPrefix = "z") { - fileExamples().toList should beEmpty - } - } - - "listing single-character prefixed files" should { - "produce matching paths only" in new directoryStructure(withCompletionPrefix = "f") { - fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) - } - } - - "listing directory-prefixed files" should { - "produce matching paths only" in new directoryStructure(withCompletionPrefix = "far") { - fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) + "listing files with a sub-path prefix" should + "produce matching paths only" in { + val _ = new DirectoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { + fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) + } } - "produce sub-dir contents only when appending a file separator to the directory" in new directoryStructure(withCompletionPrefix = "far" + File.separator) { - fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) + "completing a full path" should + "produce a list with an empty string" in { + val _ = new DirectoryStructure(withCompletionPrefix = "bazaar") { + fileExamples().toList shouldEqual List("") + } } - } - "listing files with a sub-path prefix" should { - "produce matching paths only" in new directoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { - fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) - } - } - - "completing a full path" should { - "produce a list with an empty string" in new directoryStructure(withCompletionPrefix = "bazaar") { - fileExamples().toList shouldEqual List("") - } - } - - class directoryStructure(withCompletionPrefix: String = "") extends Scope with DelayedInit { + class DirectoryStructure(withCompletionPrefix: String = "") extends DelayedInit { var fileExamples: FileExamples = _ var baseDir: File = _ var childFiles: List[File] = _ diff --git a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala index 4323b8e24..db1ebe67f 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala @@ -1,26 +1,23 @@ package sbt.util.internal package complete -import org.specs2.mutable.Specification -import org.specs2.specification.Scope +class FixedSetExamplesTest extends UnitSpec { -class FixedSetExamplesTest extends Specification { - - "adding a prefix" should { - "produce a smaller set of examples with the prefix removed" in new examples { - fixedSetExamples.withAddedPrefix("f")() must containTheSameElementsAs(List("oo", "ool", "u")) - fixedSetExamples.withAddedPrefix("fo")() must containTheSameElementsAs(List("o", "ol")) - fixedSetExamples.withAddedPrefix("b")() must containTheSameElementsAs(List("ar")) + "adding a prefix" should "produce a smaller set of examples with the prefix removed" in { + val _ = new Examples { + fixedSetExamples.withAddedPrefix("f")() should contain theSameElementsAs (List("oo", "ool", "u")) + fixedSetExamples.withAddedPrefix("fo")() should contain theSameElementsAs (List("o", "ol")) + fixedSetExamples.withAddedPrefix("b")() should contain theSameElementsAs (List("ar")) } } - "without a prefix" should { - "produce the original set" in new examples { - fixedSetExamples() mustEqual exampleSet + "without a prefix" should "produce the original set" in { + val _ = new Examples { + fixedSetExamples() shouldBe exampleSet } } - trait examples extends Scope { + trait Examples { val exampleSet = List("foo", "bar", "fool", "fu") val fixedSetExamples = FixedSetExamples(exampleSet) } diff --git a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala index 854569e88..fc7c0ae90 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -1,61 +1,64 @@ package sbt.util.internal package complete -import org.specs2.mutable.Specification -import org.specs2.specification.Scope import Completion._ -class ParserWithExamplesTest extends Specification { +class ParserWithExamplesTest extends UnitSpec { - "listing a limited number of completions" should { - "grab only the needed number of elements from the iterable source of examples" in new parserWithLazyExamples { - parserWithExamples.completions(0) - examples.size shouldEqual maxNumberOfExamples + "listing a limited number of completions" should + "grab only the needed number of elements from the iterable source of examples" in { + val _ = new ParserWithLazyExamples { + parserWithExamples.completions(0) + examples.size shouldEqual maxNumberOfExamples + } } - } - "listing only valid completions" should { - "use the delegate parser to remove invalid examples" in new parserWithValidExamples { - val validCompletions = Completions(Set( - suggestion("blue"), - suggestion("red"))) - parserWithExamples.completions(0) shouldEqual validCompletions + "listing only valid completions" should + "use the delegate parser to remove invalid examples" in { + val _ = new ParserWithValidExamples { + val validCompletions = Completions(Set( + suggestion("blue"), + suggestion("red"))) + parserWithExamples.completions(0) shouldEqual validCompletions + } } - } - "listing valid completions in a derived parser" should { - "produce only valid examples that start with the character of the derivation" in new parserWithValidExamples { - val derivedCompletions = Completions(Set( - suggestion("lue"))) - parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions + "listing valid completions in a derived parser" should + "produce only valid examples that start with the character of the derivation" in { + val _ = new ParserWithValidExamples { + val derivedCompletions = Completions(Set( + suggestion("lue"))) + parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions + } } - } - "listing valid and invalid completions" should { - "produce the entire source of examples" in new parserWithAllExamples { - val completions = Completions(examples.map(suggestion(_)).toSet) - parserWithExamples.completions(0) shouldEqual completions + "listing valid and invalid completions" should + "produce the entire source of examples" in { + val _ = new parserWithAllExamples { + val completions = Completions(examples.map(suggestion(_)).toSet) + parserWithExamples.completions(0) shouldEqual completions + } } - } - "listing valid and invalid completions in a derived parser" should { - "produce only examples that start with the character of the derivation" in new parserWithAllExamples { - val derivedCompletions = Completions(Set( - suggestion("lue"), - suggestion("lock"))) - parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions + "listing valid and invalid completions in a derived parser" should + "produce only examples that start with the character of the derivation" in { + val _ = new parserWithAllExamples { + val derivedCompletions = Completions(Set( + suggestion("lue"), + suggestion("lock"))) + parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions + } } - } - class parserWithLazyExamples extends parser(GrowableSourceOfExamples(), maxNumberOfExamples = 5, removeInvalidExamples = false) + class ParserWithLazyExamples extends ParserExample(GrowableSourceOfExamples(), maxNumberOfExamples = 5, removeInvalidExamples = false) - class parserWithValidExamples extends parser(removeInvalidExamples = true) + class ParserWithValidExamples extends ParserExample(removeInvalidExamples = true) - class parserWithAllExamples extends parser(removeInvalidExamples = false) + class parserWithAllExamples extends ParserExample(removeInvalidExamples = false) - case class parser(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), + case class ParserExample(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), maxNumberOfExamples: Int = 25, - removeInvalidExamples: Boolean) extends Scope { + removeInvalidExamples: Boolean) { import DefaultParsers._ From 3bb53ae4fc50ffd7218a5bb465f0bd3f32060a10 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 5 Sep 2015 00:51:58 -0400 Subject: [PATCH 12/50] sbt.util.internal -> sbt.internal.util package --- .../scala/sbt/{util/internal => internal/util}/AList.scala | 2 +- .../sbt/{util/internal => internal/util}/Attributes.scala | 2 +- .../sbt/{util/internal => internal/util}/Classes.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/Dag.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/HList.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/IDSet.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/INode.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/KList.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/PMap.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/Param.scala | 2 +- .../sbt/{util/internal => internal/util}/Positions.scala | 2 +- .../sbt/{util/internal => internal/util}/Settings.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/Show.scala | 2 +- .../sbt/{util/internal => internal/util}/ShowLines.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/Signal.scala | 2 +- .../{util/internal => internal/util}/TypeFunctions.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/Types.scala | 2 +- .../scala/sbt/{util/internal => internal/util}/Util.scala | 2 +- .../util-collection/src/test/scala/DagSpecification.scala | 2 +- internal/util-collection/src/test/scala/KeyTest.scala | 2 +- internal/util-collection/src/test/scala/LiteralTest.scala | 2 +- internal/util-collection/src/test/scala/PMapTest.scala | 2 +- .../util-collection/src/test/scala/SettingsExample.scala | 2 +- internal/util-collection/src/test/scala/SettingsTest.scala | 2 +- .../sbt/{util/internal => internal/util}/LineReader.scala | 4 ++-- .../internal => internal/util}/complete/Completions.scala | 2 +- .../internal => internal/util}/complete/EditDistance.scala | 2 +- .../internal => internal/util}/complete/ExampleSource.scala | 2 +- .../{util/internal => internal/util}/complete/History.scala | 2 +- .../util}/complete/HistoryCommands.scala | 2 +- .../util}/complete/JLineCompletion.scala | 2 +- .../{util/internal => internal/util}/complete/Parser.scala | 6 +++--- .../{util/internal => internal/util}/complete/Parsers.scala | 2 +- .../internal => internal/util}/complete/ProcessError.scala | 2 +- .../util}/complete/TokenCompletions.scala | 2 +- .../internal => internal/util}/complete/TypeString.scala | 2 +- .../internal => internal/util}/complete/UpperBound.scala | 2 +- internal/util-complete/src/test/scala/ParserTest.scala | 2 +- .../src/test/scala/sbt/complete/FileExamplesTest.scala | 2 +- .../src/test/scala/sbt/complete/FixedSetExamplesTest.scala | 2 +- .../test/scala/sbt/complete/ParserWithExamplesTest.scala | 2 +- .../sbt/{util/internal => internal/util}/logic/Logic.scala | 2 +- internal/util-logic/src/test/scala/sbt/logic/Test.scala | 2 +- 43 files changed, 46 insertions(+), 46 deletions(-) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/AList.scala (99%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Attributes.scala (99%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Classes.scala (97%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Dag.scala (99%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/HList.scala (96%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/IDSet.scala (98%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/INode.scala (99%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/KList.scala (98%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/PMap.scala (99%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Param.scala (95%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Positions.scala (94%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Settings.scala (99%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Show.scala (84%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/ShowLines.scala (92%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Signal.scala (99%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/TypeFunctions.scala (98%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Types.scala (88%) rename internal/util-collection/src/main/scala/sbt/{util/internal => internal/util}/Util.scala (98%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/LineReader.scala (98%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/Completions.scala (99%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/EditDistance.scala (97%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/ExampleSource.scala (99%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/History.scala (98%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/HistoryCommands.scala (98%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/JLineCompletion.scala (99%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/Parser.scala (99%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/Parsers.scala (99%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/ProcessError.scala (97%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/TokenCompletions.scala (98%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/TypeString.scala (99%) rename internal/util-complete/src/main/scala/sbt/{util/internal => internal/util}/complete/UpperBound.scala (98%) rename internal/util-logic/src/main/scala/sbt/{util/internal => internal/util}/logic/Logic.scala (99%) diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/AList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/util/internal/AList.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/AList.scala index e825b385c..3247e9a8a 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/AList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util import Classes.Applicative import Types._ diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Attributes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/util/internal/Attributes.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index fb4bb8eaa..817896567 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import Types._ import scala.reflect.Manifest diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Classes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Classes.scala similarity index 97% rename from internal/util-collection/src/main/scala/sbt/util/internal/Classes.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Classes.scala index 678eb0651..b44cb8606 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Classes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Classes.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util object Classes { trait Applicative[M[_]] { diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Dag.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/util/internal/Dag.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala index d41730e88..3a6e1d414 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Dag.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2009, 2010 David MacIver, Mark Harrah */ -package sbt.util.internal +package sbt.internal.util trait Dag[Node <: Dag[Node]] { self: Node => diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/HList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala similarity index 96% rename from internal/util-collection/src/main/scala/sbt/util/internal/HList.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/HList.scala index 01cded498..37c19dfdc 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/HList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import Types._ diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/IDSet.scala b/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala similarity index 98% rename from internal/util-collection/src/main/scala/sbt/util/internal/IDSet.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala index cefe13186..d7a9f7c1a 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/IDSet.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util /** A mutable set interface that uses object identity to test for set membership.*/ trait IDSet[T] { diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/INode.scala b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/util/internal/INode.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/INode.scala index 3e2310f2f..d7a15eee8 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/INode.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util import java.lang.Runnable import java.util.concurrent.{ atomic, Executor, LinkedBlockingQueue } diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/KList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala similarity index 98% rename from internal/util-collection/src/main/scala/sbt/util/internal/KList.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/KList.scala index 1df8c4df5..5530ba0bc 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/KList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util import Types._ import Classes.Applicative diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/PMap.scala b/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/util/internal/PMap.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala index f9ac4b0a0..a62755544 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/PMap.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import collection.mutable diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Param.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala similarity index 95% rename from internal/util-collection/src/main/scala/sbt/util/internal/Param.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Param.scala index 16bd17e49..08a58c837 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Param.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import Types._ diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Positions.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala similarity index 94% rename from internal/util-collection/src/main/scala/sbt/util/internal/Positions.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala index fd64e4538..a11ae9c24 100755 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Positions.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util sealed trait SourcePosition diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/util/internal/Settings.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index 9e6cdf95e..f742a778c 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import Types._ diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Show.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Show.scala similarity index 84% rename from internal/util-collection/src/main/scala/sbt/util/internal/Show.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Show.scala index 930431e13..4a0343ed7 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Show.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Show.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util trait Show[T] { def apply(t: T): String diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/ShowLines.scala b/internal/util-collection/src/main/scala/sbt/internal/util/ShowLines.scala similarity index 92% rename from internal/util-collection/src/main/scala/sbt/util/internal/ShowLines.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/ShowLines.scala index 794f3d5cb..f99a1394c 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/ShowLines.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/ShowLines.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util trait ShowLines[A] { def showLines(a: A): Seq[String] diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Signal.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala similarity index 99% rename from internal/util-collection/src/main/scala/sbt/util/internal/Signal.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala index f8e3ed17d..8631fc75b 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Signal.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util object Signals { val CONT = "CONT" diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/TypeFunctions.scala b/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala similarity index 98% rename from internal/util-collection/src/main/scala/sbt/util/internal/TypeFunctions.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala index ed1669f35..b7aac6360 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/TypeFunctions.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util trait TypeFunctions { type Id[X] = X diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Types.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Types.scala similarity index 88% rename from internal/util-collection/src/main/scala/sbt/util/internal/Types.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Types.scala index 972f12769..9b6eb0733 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Types.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Types.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util object Types extends Types diff --git a/internal/util-collection/src/main/scala/sbt/util/internal/Util.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala similarity index 98% rename from internal/util-collection/src/main/scala/sbt/util/internal/Util.scala rename to internal/util-collection/src/main/scala/sbt/internal/util/Util.scala index ba158ae57..4f82cae9c 100644 --- a/internal/util-collection/src/main/scala/sbt/util/internal/Util.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import java.util.Locale diff --git a/internal/util-collection/src/test/scala/DagSpecification.scala b/internal/util-collection/src/test/scala/DagSpecification.scala index 47c1b802b..9e5025488 100644 --- a/internal/util-collection/src/test/scala/DagSpecification.scala +++ b/internal/util-collection/src/test/scala/DagSpecification.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import org.scalacheck._ import Prop._ diff --git a/internal/util-collection/src/test/scala/KeyTest.scala b/internal/util-collection/src/test/scala/KeyTest.scala index 8bdcd76bf..461655b88 100644 --- a/internal/util-collection/src/test/scala/KeyTest.scala +++ b/internal/util-collection/src/test/scala/KeyTest.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util import org.scalacheck._ import Prop._ diff --git a/internal/util-collection/src/test/scala/LiteralTest.scala b/internal/util-collection/src/test/scala/LiteralTest.scala index 32da58a7b..b50d02632 100644 --- a/internal/util-collection/src/test/scala/LiteralTest.scala +++ b/internal/util-collection/src/test/scala/LiteralTest.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import Types._ diff --git a/internal/util-collection/src/test/scala/PMapTest.scala b/internal/util-collection/src/test/scala/PMapTest.scala index e8aa2c6fa..9e1dde2c9 100644 --- a/internal/util-collection/src/test/scala/PMapTest.scala +++ b/internal/util-collection/src/test/scala/PMapTest.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import Types._ diff --git a/internal/util-collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala index 96a8e01f6..f7b2f2cf0 100644 --- a/internal/util-collection/src/test/scala/SettingsExample.scala +++ b/internal/util-collection/src/test/scala/SettingsExample.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util /** Define our settings system */ diff --git a/internal/util-collection/src/test/scala/SettingsTest.scala b/internal/util-collection/src/test/scala/SettingsTest.scala index 3321c644b..8b77dba16 100644 --- a/internal/util-collection/src/test/scala/SettingsTest.scala +++ b/internal/util-collection/src/test/scala/SettingsTest.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util import org.scalacheck._ import Prop._ diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/util/internal/LineReader.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index c01986a0f..18cc431d7 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2009 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util import jline.console.ConsoleReader import jline.console.history.{ FileHistory, MemoryHistory } @@ -128,7 +128,7 @@ final class FullReader(historyPath: Option[File], complete: Parser[_], val handl protected[this] val reader = { val cr = JLine.createReader(historyPath) - sbt.util.internal.complete.JLineCompletion.installCustomCompletor(cr, complete) + sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete) cr } } diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/Completions.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/Completions.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala index b3a562dc7..c035f3620 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/Completions.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util package complete /** diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/EditDistance.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala similarity index 97% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/EditDistance.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala index 873d9035c..8cb617348 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/EditDistance.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete import java.lang.Character.{ toLowerCase => lower } diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/ExampleSource.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/ExampleSource.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala index 932951e34..6539554a6 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/ExampleSource.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete import java.io.File diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/History.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/History.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala index bd925cd17..350c58dfa 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/History.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util package complete import History.number diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/HistoryCommands.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala index 662b914c0..f74d4e448 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util package complete import java.io.File diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/JLineCompletion.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/JLineCompletion.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala index c674e21db..e098f59f6 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/JLineCompletion.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util package complete import jline.console.ConsoleReader diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/Parser.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index 659f20e7b..a41c0d7d2 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -1,12 +1,12 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2010, 2011 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util package complete import Parser._ -import sbt.util.internal.Types.{ left, right, some } -import sbt.util.internal.Util.{ makeList, separate } +import sbt.internal.util.Types.{ left, right, some } +import sbt.internal.util.Util.{ makeList, separate } /** * A String parser that provides semi-automatic tab completion. diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/Parsers.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala index e0b81884f..9463d1acb 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2011 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util package complete import Parser._ diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/ProcessError.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala similarity index 97% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/ProcessError.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala index 2faa17634..6d74ed2d2 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/ProcessError.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete object ProcessError { diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/TokenCompletions.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/TokenCompletions.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala index 6d3e5100f..0d0b2980e 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/TokenCompletions.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete import Completion.{ token => ctoken, tokenDisplay } diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/TypeString.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala similarity index 99% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/TypeString.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala index 1ac092023..9a308a2bf 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/TypeString.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete import DefaultParsers._ diff --git a/internal/util-complete/src/main/scala/sbt/util/internal/complete/UpperBound.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala similarity index 98% rename from internal/util-complete/src/main/scala/sbt/util/internal/complete/UpperBound.scala rename to internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala index cd42e688b..6b600f9ed 100644 --- a/internal/util-complete/src/main/scala/sbt/util/internal/complete/UpperBound.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008,2010 Mark Harrah */ -package sbt.util.internal +package sbt.internal.util package complete sealed trait UpperBound { diff --git a/internal/util-complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala index c1bd8c45c..1db99b513 100644 --- a/internal/util-complete/src/test/scala/ParserTest.scala +++ b/internal/util-complete/src/test/scala/ParserTest.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete object JLineTest { diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index 2f873cf6c..2af9388a7 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete import java.io.File diff --git a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala index db1ebe67f..b043497db 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete class FixedSetExamplesTest extends UnitSpec { diff --git a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala index fc7c0ae90..684cbe403 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package complete import Completion._ diff --git a/internal/util-logic/src/main/scala/sbt/util/internal/logic/Logic.scala b/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala similarity index 99% rename from internal/util-logic/src/main/scala/sbt/util/internal/logic/Logic.scala rename to internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala index 3e6f8ba8c..795423c54 100644 --- a/internal/util-logic/src/main/scala/sbt/util/internal/logic/Logic.scala +++ b/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package logic import scala.annotation.tailrec diff --git a/internal/util-logic/src/test/scala/sbt/logic/Test.scala b/internal/util-logic/src/test/scala/sbt/logic/Test.scala index f6170f1e3..59b40c34b 100644 --- a/internal/util-logic/src/test/scala/sbt/logic/Test.scala +++ b/internal/util-logic/src/test/scala/sbt/logic/Test.scala @@ -1,4 +1,4 @@ -package sbt.util.internal +package sbt.internal.util package logic import org.scalacheck._ From 6eed17ad4ed285081268c848a5d67ec29ba61f08 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Thu, 10 Sep 2015 14:39:29 +0200 Subject: [PATCH 13/50] Make `sbt.internal.util.JLine` private to sbt package It was private to `sbt.internal.util`, but it is used in sbt's codebase. --- .../src/main/scala/sbt/internal/util/LineReader.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index 18cc431d7..e72341a23 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -58,7 +58,7 @@ abstract class JLine extends LineReader { reader.flush() } } -private object JLine { +private[sbt] object JLine { private[this] val TerminalProperty = "jline.terminal" fixTerminalProperty() From 2cefe315f2a62522a2995273140f38fe0a1a13cd Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 16 Sep 2015 22:06:13 -0400 Subject: [PATCH 14/50] New house rules --- .../src/main/scala/sbt/internal/util/Dag.scala | 5 ++--- .../src/test/scala/SettingsExample.scala | 3 ++- .../src/test/scala/SettingsTest.scala | 9 ++++++--- .../util/complete/HistoryCommands.scala | 6 +++--- .../sbt/internal/util/complete/Parser.scala | 9 ++++----- .../internal/util/complete/TypeString.scala | 3 ++- .../sbt/complete/ParserWithExamplesTest.scala | 18 ++++++++++++------ .../scala/sbt/internal/util/logic/Logic.scala | 3 ++- .../src/test/scala/sbt/logic/Test.scala | 3 ++- 9 files changed, 35 insertions(+), 24 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala index 3a6e1d414..5cad287da 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala @@ -56,9 +56,8 @@ object Dag { finished; } final class Cyclic(val value: Any, val all: List[Any], val complete: Boolean) - extends Exception("Cyclic reference involving " + - (if (complete) all.mkString("\n ", "\n ", "") else value) - ) { + extends Exception("Cyclic reference involving " + + (if (complete) all.mkString("\n ", "\n ", "") else value)) { def this(value: Any) = this(value, value :: Nil, false) override def toString = getMessage def ::(a: Any): Cyclic = diff --git a/internal/util-collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala index f7b2f2cf0..5dd408282 100644 --- a/internal/util-collection/src/test/scala/SettingsExample.scala +++ b/internal/util-collection/src/test/scala/SettingsExample.scala @@ -49,7 +49,8 @@ object SettingsUsage { val mySettings: Seq[Setting[_]] = Seq( setting(a3, value(3)), setting(b4, map(a4)(_ * 3)), - update(a5)(_ + 1)) + update(a5)(_ + 1) + ) // "compiles" and applies the settings. // This can be split into multiple steps to access intermediate results if desired. diff --git a/internal/util-collection/src/test/scala/SettingsTest.scala b/internal/util-collection/src/test/scala/SettingsTest.scala index 8b77dba16..85a3760ee 100644 --- a/internal/util-collection/src/test/scala/SettingsTest.scala +++ b/internal/util-collection/src/test/scala/SettingsTest.scala @@ -55,7 +55,8 @@ object SettingsTest extends Properties("settings") { List(scoped0, scoped1) <- chk :: scopedKeys sliding 2 nextInit = if (scoped0 == chk) chk else (scoped0 zipWith chk) { (p, _) => p + 1 } - } yield derive(setting(scoped1, nextInit))).toSeq + } yield derive(setting(scoped1, nextInit)) + ).toSeq { // Note: This causes a cycle refernec error, quite frequently. @@ -95,7 +96,8 @@ object SettingsTest extends Properties("settings") { setting(b, value(6)), derive(setting(b, a)), setting(a, value(5)), - setting(b, value(8))) + setting(b, value(8)) + ) val ev = evaluate(settings) checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev) } @@ -104,7 +106,8 @@ object SettingsTest extends Properties("settings") { setting(a, value(3)), setting(b, value(6)), derive(setting(b, a)), - setting(a, value(5))) + setting(a, value(5)) + ) val ev = evaluate(settings) checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev) } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala index f74d4e448..350a36610 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala @@ -34,7 +34,8 @@ object HistoryCommands { Nth -> ("Execute the command with index n, as shown by the " + ListFull + " command"), Previous -> "Execute the nth command before this one", StartsWithString -> "Execute the most recent command starting with 'string'", - ContainsString -> "Execute the most recent command containing 'string'") + ContainsString -> "Execute the most recent command containing 'string'" + ) def helpString = "History commands:\n " + (descriptions.map { case (c, d) => c + " " + d }).mkString("\n ") def printHelp(): Unit = println(helpString) @@ -46,8 +47,7 @@ object HistoryCommands { val MaxLines = 500 lazy val num = token(NatBasic, "") lazy val last = Last ^^^ { execute(_.!!) } - lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => - (h: History) => { printHistory(h, MaxLines, show); Some(Nil) } + lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => (h: History) => { printHistory(h, MaxLines, show); Some(Nil) } } lazy val execStr = flag('?') ~ token(any.+.string, "") map { case (contains, str) => diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index a41c0d7d2..a6d5474aa 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -426,11 +426,10 @@ trait ParserMain { case _ => val ci = i + 1 if (ci >= s.length) - a.resultEmpty.toEither.left.map { msgs0 => - () => - val msgs = msgs0() - val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs - (nonEmpty, ci) + a.resultEmpty.toEither.left.map { msgs0 => () => + val msgs = msgs0() + val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs + (nonEmpty, ci) } else loop(ci, a derive s(ci)) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala index 9a308a2bf..e96dbad4f 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala @@ -62,7 +62,8 @@ private[sbt] object TypeString { val TypeMap = Map( "java.io.File" -> "File", "java.net.URL" -> "URL", - "java.net.URI" -> "URI") + "java.net.URI" -> "URI" + ) /** * A Parser that extracts basic structure from the string representation of a type from Manifest.toString. diff --git a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala index 684cbe403..17891be4f 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -18,7 +18,8 @@ class ParserWithExamplesTest extends UnitSpec { val _ = new ParserWithValidExamples { val validCompletions = Completions(Set( suggestion("blue"), - suggestion("red"))) + suggestion("red") + )) parserWithExamples.completions(0) shouldEqual validCompletions } } @@ -27,7 +28,8 @@ class ParserWithExamplesTest extends UnitSpec { "produce only valid examples that start with the character of the derivation" in { val _ = new ParserWithValidExamples { val derivedCompletions = Completions(Set( - suggestion("lue"))) + suggestion("lue") + )) parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions } } @@ -45,7 +47,8 @@ class ParserWithExamplesTest extends UnitSpec { val _ = new parserWithAllExamples { val derivedCompletions = Completions(Set( suggestion("lue"), - suggestion("lock"))) + suggestion("lock") + )) parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions } } @@ -56,9 +59,11 @@ class ParserWithExamplesTest extends UnitSpec { class parserWithAllExamples extends ParserExample(removeInvalidExamples = false) - case class ParserExample(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), + case class ParserExample( + examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), maxNumberOfExamples: Int = 25, - removeInvalidExamples: Boolean) { + removeInvalidExamples: Boolean + ) { import DefaultParsers._ @@ -67,7 +72,8 @@ class ParserWithExamplesTest extends UnitSpec { colorParser, FixedSetExamples(examples), maxNumberOfExamples, - removeInvalidExamples) + removeInvalidExamples + ) } case class GrowableSourceOfExamples() extends Iterable[String] { diff --git a/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala b/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala index 795423c54..0e15fadf2 100644 --- a/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala +++ b/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala @@ -103,7 +103,8 @@ object Logic { checkAcyclic(clauses) problem.toLeft( - reduce0(clauses, initialFacts, Matched.empty)) + reduce0(clauses, initialFacts, Matched.empty) + ) } /** diff --git a/internal/util-logic/src/test/scala/sbt/logic/Test.scala b/internal/util-logic/src/test/scala/sbt/logic/Test.scala index 59b40c34b..91ded0e69 100644 --- a/internal/util-logic/src/test/scala/sbt/logic/Test.scala +++ b/internal/util-logic/src/test/scala/sbt/logic/Test.scala @@ -20,7 +20,8 @@ object LogicTest extends Properties("Logic") { case Right(res) => false case Left(err: Logic.CyclicNegation) => true case Left(err) => sys.error(s"Expected cyclic error, got: $err") - }) + } + ) def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match { case Left(err) => false From 7a6253a5c37327cea1b3e68030f9c50770d1f158 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 30 Sep 2015 21:59:20 -0400 Subject: [PATCH 15/50] Fixes sbt/util#14 by rolling back 6175d9233848b220ad8b68f63c90e9b844903f0d --- .../src/main/scala/sbt/internal/util/Settings.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index f742a778c..6519287da 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -3,6 +3,8 @@ */ package sbt.internal.util +import scala.language.existentials + import Types._ sealed trait Settings[Scope] { @@ -445,7 +447,7 @@ trait Init[Scope] { def join: Initialize[Seq[T]] = uniform(s)(idFun) } def join[T](inits: Seq[Initialize[T]]): Initialize[Seq[T]] = uniform(inits)(idFun) - def joinAny[M[_], T](inits: Seq[Initialize[M[T]]]): Initialize[Seq[M[_]]] = + def joinAny[M[_]](inits: Seq[Initialize[M[T]] forSome { type T }]): Initialize[Seq[M[_]]] = join(inits.asInstanceOf[Seq[Initialize[M[Any]]]]).asInstanceOf[Initialize[Seq[M[T] forSome { type T }]]] } object SettingsDefinition { From db08c77660f8bd12e061870271c036dbdb92b4a1 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 14 Feb 2016 23:00:25 +0000 Subject: [PATCH 16/50] Unexecute Positions --- .../src/main/scala/sbt/internal/util/Positions.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala old mode 100755 new mode 100644 From 972450db5d4ccdf9f8a6bced572b15465c0a993b Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 18 Mar 2016 02:38:06 -0400 Subject: [PATCH 17/50] Refactor nulls to Option --- .../scala/sbt/internal/util/LineReader.scala | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index e72341a23..ed68630bd 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -15,13 +15,12 @@ abstract class JLine extends LineReader { def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) } - private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]) = - readLineWithHistory(prompt, mask) match { - case null => None - case x => Some(x.trim) + private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]): Option[String] = + readLineWithHistory(prompt, mask) map { x => + x.trim } - private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): String = + private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): Option[String] = reader.getHistory match { case fh: FileHistory => try { readLineDirect(prompt, mask) } @@ -29,17 +28,17 @@ abstract class JLine extends LineReader { case _ => readLineDirect(prompt, mask) } - private[this] def readLineDirect(prompt: String, mask: Option[Char]): String = + private[this] def readLineDirect(prompt: String, mask: Option[Char]): Option[String] = if (handleCONT) Signals.withHandler(() => resume(), signal = Signals.CONT)(() => readLineDirectRaw(prompt, mask)) else readLineDirectRaw(prompt, mask) - private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): String = + private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] = { val newprompt = handleMultilinePrompt(prompt) mask match { - case Some(m) => reader.readLine(newprompt, m) - case None => reader.readLine(newprompt) + case Some(m) => Option(reader.readLine(newprompt, m)) + case None => Option(reader.readLine(newprompt)) } } From 1dfd9db2c7ac6d86c343aa764b386076924e408f Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 18 Mar 2016 21:58:40 -0400 Subject: [PATCH 18/50] Inject Thread.sleep periodically during read() to allow thread interruption --- .../scala/sbt/internal/util/LineReader.scala | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index ed68630bd..93552b518 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -5,9 +5,11 @@ package sbt.internal.util import jline.console.ConsoleReader import jline.console.history.{ FileHistory, MemoryHistory } -import java.io.{ File, InputStream, PrintWriter } +import java.io.{ File, InputStream, PrintWriter, FileInputStream, FileDescriptor, FilterInputStream } import complete.Parser import java.util.concurrent.atomic.AtomicBoolean +import scala.concurrent.duration.Duration +import scala.annotation.tailrec abstract class JLine extends LineReader { protected[this] val handleCONT: Boolean @@ -94,10 +96,14 @@ private[sbt] object JLine { t.restore f(t) } - def createReader(): ConsoleReader = createReader(None) - def createReader(historyPath: Option[File]): ConsoleReader = + def createReader(): ConsoleReader = createReader(None, true) + def createReader(historyPath: Option[File], injectThreadSleep: Boolean): ConsoleReader = usingTerminal { t => - val cr = new ConsoleReader + val cr = if (injectThreadSleep) { + val originalIn = new FileInputStream(FileDescriptor.in) + val in = new InputStreamWrapper(originalIn, Duration("50 ms")) + new ConsoleReader(in, System.out) + } else new ConsoleReader cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650 cr.setBellEnabled(false) val h = historyPath match { @@ -115,25 +121,44 @@ private[sbt] object JLine { finally { t.restore } } - def simple(historyPath: Option[File], handleCONT: Boolean = HandleCONT): SimpleReader = new SimpleReader(historyPath, handleCONT) + def simple( + historyPath: Option[File], + handleCONT: Boolean = HandleCONT, + injectThreadSleep: Boolean = true + ): SimpleReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep) val MaxHistorySize = 500 val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT) } +private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration) extends FilterInputStream(is) { + @tailrec + final override def read(): Int = + if (is.available() != 0) is.read() + else { + Thread.sleep(poll.toMillis) + read() + } +} + trait LineReader { def readLine(prompt: String, mask: Option[Char] = None): Option[String] } -final class FullReader(historyPath: Option[File], complete: Parser[_], val handleCONT: Boolean = JLine.HandleCONT) extends JLine { +final class FullReader( + historyPath: Option[File], + complete: Parser[_], + val handleCONT: Boolean = JLine.HandleCONT, + val injectThreadSleep: Boolean = true +) extends JLine { protected[this] val reader = { - val cr = JLine.createReader(historyPath) + val cr = JLine.createReader(historyPath, injectThreadSleep) sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete) cr } } -class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean) extends JLine { - protected[this] val reader = JLine.createReader(historyPath) +class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean, val injectThreadSleep: Boolean) extends JLine { + protected[this] val reader = JLine.createReader(historyPath, injectThreadSleep) } -object SimpleReader extends SimpleReader(None, JLine.HandleCONT) +object SimpleReader extends SimpleReader(None, JLine.HandleCONT, true) From 5811e8a2579b4822f0f247dd843b346a76e00780 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 31 Mar 2016 01:28:55 -0400 Subject: [PATCH 19/50] Fixes #32. Don't inject thread sleep by default. Thread sleeping interferes with scripted test when the build cannot be loaded. The scripted test gets stuck, and jstack shows java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at sbt.internal.util.InputStreamWrapper.read(LineReader.scala:138) at jline.internal.NonBlockingInputStream.read(NonBlockingInputStream.java:2 45) .... at sbt.internal.util.JLine$.withJLine(LineReader.scala:118) at sbt.internal.util.JLine.readLine(LineReader.scala:18) at sbt.BuiltinCommands$.sbt$BuiltinCommands$$doLoadFailed(Main.scala:460) --- .../src/main/scala/sbt/internal/util/LineReader.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index 93552b518..712071684 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -124,7 +124,7 @@ private[sbt] object JLine { def simple( historyPath: Option[File], handleCONT: Boolean = HandleCONT, - injectThreadSleep: Boolean = true + injectThreadSleep: Boolean = false ): SimpleReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep) val MaxHistorySize = 500 val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT) @@ -147,7 +147,7 @@ final class FullReader( historyPath: Option[File], complete: Parser[_], val handleCONT: Boolean = JLine.HandleCONT, - val injectThreadSleep: Boolean = true + val injectThreadSleep: Boolean = false ) extends JLine { protected[this] val reader = { @@ -160,5 +160,5 @@ final class FullReader( class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean, val injectThreadSleep: Boolean) extends JLine { protected[this] val reader = JLine.createReader(historyPath, injectThreadSleep) } -object SimpleReader extends SimpleReader(None, JLine.HandleCONT, true) +object SimpleReader extends SimpleReader(None, JLine.HandleCONT, false) From f76b2d05d57fdc67ff8160a657cd37b2a31fda23 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 29 Mar 2016 23:13:45 -0400 Subject: [PATCH 20/50] Add Eval from Cats - https://github.com/typelevel/cats/blob/a8ba943fff5928d962101a92d5c173d51df1d626/core/src/main/scala/cats/Eval.scala --- .../main/scala/sbt/internal/util/Eval.scala | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala new file mode 100644 index 000000000..5117f1a6d --- /dev/null +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala @@ -0,0 +1,264 @@ +package sbt.internal.util + +import scala.annotation.tailrec + +// Copied from Cats (MIT license) + +/** + * Eval is a datatype, which controls evaluation. + */ +sealed abstract class Eval[A] extends Serializable { self => + /** + * Evaluate the computation and return an A value. + * + * For lazy instances (Later, Always), any necessary computation + * will be performed at this point. For eager instances (Now), a + * value will be immediately returned. + */ + def value: A + + /** + * Transform an Eval[A] into an Eval[B] given the transformation + * function `f`. + * + * This call is stack-safe -- many .map calls may be chained without + * consumed additional stack during evaluation. + * + * Computation performed in f is always lazy, even when called on an + * eager (Now) instance. + */ + def map[B](f: A => B): Eval[B] = + flatMap(a => Now(f(a))) + + /** + * Lazily perform a computation based on an Eval[A], using the + * function `f` to produce an Eval[B] given an A. + * + * This call is stack-safe -- many .flatMap calls may be chained + * without consumed additional stack during evaluation. It is also + * written to avoid left-association problems, so that repeated + * calls to .flatMap will be efficiently applied. + * + * Computation performed in f is always lazy, even when called on an + * eager (Now) instance. + */ + def flatMap[B](f: A => Eval[B]): Eval[B] = + this match { + case c: Eval.Compute[A] => + new Eval.Compute[B] { + type Start = c.Start + val start = c.start + val run = (s: c.Start) => + new Eval.Compute[B] { + type Start = A + val start = () => c.run(s) + val run = f + } + } + case c: Eval.Call[A] => + new Eval.Compute[B] { + type Start = A + val start = c.thunk + val run = f + } + case _ => + new Eval.Compute[B] { + type Start = A + val start = () => self + val run = f + } + } + + /** + * Ensure that the result of the computation (if any) will be + * memoized. + * + * Practically, this means that when called on an Always[A] a + * Later[A] with an equivalent computation will be returned. + */ + def memoize: Eval[A] +} + +/** + * Construct an eager Eval[A] instance. + * + * In some sense it is equivalent to using a val. + * + * This type should be used when an A value is already in hand, or + * when the computation to produce an A value is pure and very fast. + */ +final case class Now[A](value: A) extends Eval[A] { + def memoize: Eval[A] = this +} + +/** + * Construct a lazy Eval[A] instance. + * + * This type should be used for most "lazy" values. In some sense it + * is equivalent to using a lazy val. + * + * When caching is not required or desired (e.g. if the value produced + * may be large) prefer Always. When there is no computation + * necessary, prefer Now. + * + * Once Later has been evaluated, the closure (and any values captured + * by the closure) will not be retained, and will be available for + * garbage collection. + */ +final class Later[A](f: () => A) extends Eval[A] { + private[this] var thunk: () => A = f + + // The idea here is that `f` may have captured very large + // structures, but produce a very small result. In this case, once + // we've calculated a value, we would prefer to be able to free + // everything else. + // + // (For situations where `f` is small, but the output will be very + // expensive to store, consider using `Always`.) + lazy val value: A = { + val result = thunk() + thunk = null // scalastyle:off + result + } + + def memoize: Eval[A] = this +} + +object Later { + def apply[A](a: => A): Later[A] = new Later(a _) +} + +/** + * Construct a lazy Eval[A] instance. + * + * This type can be used for "lazy" values. In some sense it is + * equivalent to using a Function0 value. + * + * This type will evaluate the computation every time the value is + * required. It should be avoided except when laziness is required and + * caching must be avoided. Generally, prefer Later. + */ +final class Always[A](f: () => A) extends Eval[A] { + def value: A = f() + def memoize: Eval[A] = new Later(f) +} + +object Always { + def apply[A](a: => A): Always[A] = new Always(a _) +} + +object Eval { + + /** + * Construct an eager Eval[A] value (i.e. Now[A]). + */ + def now[A](a: A): Eval[A] = Now(a) + + /** + * Construct a lazy Eval[A] value with caching (i.e. Later[A]). + */ + def later[A](a: => A): Eval[A] = new Later(a _) + + /** + * Construct a lazy Eval[A] value without caching (i.e. Always[A]). + */ + def always[A](a: => A): Eval[A] = new Always(a _) + + /** + * Defer a computation which produces an Eval[A] value. + * + * This is useful when you want to delay execution of an expression + * which produces an Eval[A] value. Like .flatMap, it is stack-safe. + */ + def defer[A](a: => Eval[A]): Eval[A] = + new Eval.Call[A](a _) {} + + /** + * Static Eval instances for some common values. + * + * These can be useful in cases where the same values may be needed + * many times. + */ + val Unit: Eval[Unit] = Now(()) + val True: Eval[Boolean] = Now(true) + val False: Eval[Boolean] = Now(false) + val Zero: Eval[Int] = Now(0) + val One: Eval[Int] = Now(1) + + /** + * Call is a type of Eval[A] that is used to defer computations + * which produce Eval[A]. + * + * Users should not instantiate Call instances themselves. Instead, + * they will be automatically created when needed. + */ + sealed abstract class Call[A](val thunk: () => Eval[A]) extends Eval[A] { + def memoize: Eval[A] = new Later(() => value) + def value: A = Call.loop(this).value + } + + object Call { + /** Collapse the call stack for eager evaluations */ + @tailrec private def loop[A](fa: Eval[A]): Eval[A] = fa match { + case call: Eval.Call[A] => + loop(call.thunk()) + case compute: Eval.Compute[A] => + new Eval.Compute[A] { + type Start = compute.Start + val start: () => Eval[Start] = () => compute.start() + val run: Start => Eval[A] = s => loop1(compute.run(s)) + } + case other => other + } + + /** + * Alias for loop that can be called in a non-tail position + * from an otherwise tailrec-optimized loop. + */ + private def loop1[A](fa: Eval[A]): Eval[A] = loop(fa) + } + + /** + * Compute is a type of Eval[A] that is used to chain computations + * involving .map and .flatMap. Along with Eval#flatMap it + * implements the trampoline that guarantees stack-safety. + * + * Users should not instantiate Compute instances + * themselves. Instead, they will be automatically created when + * needed. + * + * Unlike a traditional trampoline, the internal workings of the + * trampoline are not exposed. This allows a slightly more efficient + * implementation of the .value method. + */ + sealed abstract class Compute[A] extends Eval[A] { + type Start + val start: () => Eval[Start] + val run: Start => Eval[A] + + def memoize: Eval[A] = Later(value) + + def value: A = { + type L = Eval[Any] + type C = Any => Eval[Any] + @tailrec def loop(curr: L, fs: List[C]): Any = + curr match { + case c: Compute[_] => + c.start() match { + case cc: Compute[_] => + loop( + cc.start().asInstanceOf[L], + cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs) + case xx => + loop(c.run(xx.value).asInstanceOf[L], fs) + } + case x => + fs match { + case f :: fs => loop(f(x.value), fs) + case Nil => x.value + } + } + loop(this.asInstanceOf[L], Nil).asInstanceOf[A] + } + } +} From 1361e4f18dfffa00146ff4ec7497ec461150a811 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 30 Mar 2016 00:11:52 -0400 Subject: [PATCH 21/50] Rename Eval#value to get --- .../main/scala/sbt/internal/util/Eval.scala | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala index 5117f1a6d..1596e457b 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala @@ -15,7 +15,7 @@ sealed abstract class Eval[A] extends Serializable { self => * will be performed at this point. For eager instances (Now), a * value will be immediately returned. */ - def value: A + def get: A /** * Transform an Eval[A] into an Eval[B] given the transformation @@ -87,7 +87,7 @@ sealed abstract class Eval[A] extends Serializable { self => * This type should be used when an A value is already in hand, or * when the computation to produce an A value is pure and very fast. */ -final case class Now[A](value: A) extends Eval[A] { +final case class Now[A](get: A) extends Eval[A] { def memoize: Eval[A] = this } @@ -115,7 +115,7 @@ final class Later[A](f: () => A) extends Eval[A] { // // (For situations where `f` is small, but the output will be very // expensive to store, consider using `Always`.) - lazy val value: A = { + lazy val get: A = { val result = thunk() thunk = null // scalastyle:off result @@ -139,7 +139,7 @@ object Later { * caching must be avoided. Generally, prefer Later. */ final class Always[A](f: () => A) extends Eval[A] { - def value: A = f() + def get: A = f() def memoize: Eval[A] = new Later(f) } @@ -193,8 +193,8 @@ object Eval { * they will be automatically created when needed. */ sealed abstract class Call[A](val thunk: () => Eval[A]) extends Eval[A] { - def memoize: Eval[A] = new Later(() => value) - def value: A = Call.loop(this).value + def memoize: Eval[A] = new Later(() => get) + def get: A = Call.loop(this).get } object Call { @@ -229,16 +229,16 @@ object Eval { * * Unlike a traditional trampoline, the internal workings of the * trampoline are not exposed. This allows a slightly more efficient - * implementation of the .value method. + * implementation of the .get method. */ sealed abstract class Compute[A] extends Eval[A] { type Start val start: () => Eval[Start] val run: Start => Eval[A] - def memoize: Eval[A] = Later(value) + def memoize: Eval[A] = Later(get) - def value: A = { + def get: A = { type L = Eval[Any] type C = Any => Eval[Any] @tailrec def loop(curr: L, fs: List[C]): Any = @@ -248,14 +248,15 @@ object Eval { case cc: Compute[_] => loop( cc.start().asInstanceOf[L], - cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs) + cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs + ) case xx => - loop(c.run(xx.value).asInstanceOf[L], fs) + loop(c.run(xx.get).asInstanceOf[L], fs) } case x => fs match { - case f :: fs => loop(f(x.value), fs) - case Nil => x.value + case f :: fs => loop(f(x.get), fs) + case Nil => x.get } } loop(this.asInstanceOf[L], Nil).asInstanceOf[A] From d9bd056a29623daafc3368f483712ea347ea4724 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 1 Apr 2016 01:07:00 -0400 Subject: [PATCH 22/50] Remove some warnings --- .../scala/sbt/internal/util/Attributes.scala | 2 +- .../main/scala/sbt/internal/util/KList.scala | 4 +-- .../main/scala/sbt/internal/util/PMap.scala | 2 +- .../main/scala/sbt/internal/util/Param.scala | 2 +- .../scala/sbt/internal/util/Settings.scala | 26 ++++++++----------- .../main/scala/sbt/internal/util/Signal.scala | 4 +-- .../scala/sbt/internal/util/LineReader.scala | 2 +- .../sbt/internal/util/complete/History.scala | 2 +- 8 files changed, 20 insertions(+), 24 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index 817896567..25cf298d7 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -89,7 +89,7 @@ object AttributeKey { def isLocal: Boolean = true def rank = Int.MaxValue } - private[sbt] final val LocalLabel = "$local" + private[sbt] final val LocalLabel = "$" + "local" } /** diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala index 5530ba0bc..3406f1b4b 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala @@ -11,7 +11,7 @@ sealed trait KList[+M[_]] { def transform[N[_]](f: M ~> N): Transform[N] /** Folds this list using a function that operates on the homogeneous type of the elements of this list. */ - def foldr[T](f: (M[_], T) => T, init: T): T = init // had trouble defining it in KNil + def foldr[B](f: (M[_], B) => B, init: B): B = init // had trouble defining it in KNil /** Applies `f` to the elements of this list in the applicative functor defined by `ap`. */ def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] @@ -39,7 +39,7 @@ final case class KCons[H, +T <: KList[M], +M[_]](head: M[H], tail: T) extends KL np.apply(np.map(g, tt), f(head)) } def :^:[A, N[x] >: M[x]](h: N[A]) = KCons(h, this) - override def foldr[T](f: (M[_], T) => T, init: T): T = f(head, tail.foldr(f, init)) + override def foldr[B](f: (M[_], B) => B, init: B): B = f(head, tail.foldr(f, init)) } sealed abstract class KNil extends KList[Nothing] { final type Transform[N[_]] = KNil diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala b/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala index a62755544..989d657e2 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala @@ -87,7 +87,7 @@ abstract class AbstractRMap[K[_], V[_]] extends RMap[K, V] { */ class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]]) extends AbstractRMap[K, V] with PMap[K, V] { def get[T](k: K[T]): Option[V[T]] = cast[T](backing.get(k)) - def update[T](k: K[T], v: V[T]) { backing(k) = v } + def update[T](k: K[T], v: V[T]): Unit = { backing(k) = v } def remove[T](k: K[T]) = cast(backing.remove(k)) def getOrUpdate[T](k: K[T], make: => V[T]) = cast[T](backing.getOrElseUpdate(k, make)) def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] = diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala index 08a58c837..68671c8ca 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala @@ -9,7 +9,7 @@ import Types._ trait Param[A[_], B[_]] { type T def in: A[T] - def ret(out: B[T]) + def ret(out: B[T]): Unit def ret: B[T] } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index 6519287da..a85a3faa6 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -421,14 +421,10 @@ trait Init[Scope] { def dependencies: Seq[ScopedKey[_]] def apply[S](g: T => S): Initialize[S] - @deprecated("Will be made private.", "0.13.2") - def mapReferenced(g: MapScoped): Initialize[T] - @deprecated("Will be made private.", "0.13.2") - def mapConstant(g: MapConstant): Initialize[T] - - @deprecated("Will be made private.", "0.13.2") - def validateReferenced(g: ValidateRef): ValidatedInit[T] = - validateKeyReferenced(new ValidateKeyRef { def apply[T](key: ScopedKey[T], selfRefOk: Boolean) = g(key) }) + private[sbt] def mapReferenced(g: MapScoped): Initialize[T] + private[sbt] def mapConstant(g: MapConstant): Initialize[T] + private[sbt] def validateReferenced(g: ValidateRef): ValidatedInit[T] = + validateKeyReferenced(new ValidateKeyRef { def apply[B](key: ScopedKey[B], selfRefOk: Boolean) = g(key) }) private[sbt] def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] @@ -482,14 +478,14 @@ trait Init[Scope] { private[sbt] def mapInitialize(f: Initialize[T] => Initialize[T]): Setting[T] = make(key, f(init), pos) override def toString = "setting(" + key + ") at " + pos - protected[this] def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = new Setting[T](key, init, pos) + protected[this] def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = new Setting[B](key, init, pos) protected[sbt] def isDerived: Boolean = false private[sbt] def setScope(s: Scope): Setting[T] = make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos) /** Turn this setting into a `DefaultSetting` if it's not already, otherwise returns `this` */ private[sbt] def default(id: => Long = nextDefaultID()): DefaultSetting[T] = DefaultSetting(key, init, pos, id) } private[Init] sealed class DerivedSetting[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, val filter: Scope => Boolean, val trigger: AttributeKey[_] => Boolean) extends Setting[T](sk, i, p) { - override def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = new DerivedSetting[T](key, init, pos, filter, trigger) + override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = new DerivedSetting[B](key, init, pos, filter, trigger) protected[sbt] override def isDerived: Boolean = true override def default(_id: => Long): DefaultSetting[T] = new DerivedSetting[T](sk, i, p, filter, trigger) with DefaultSetting[T] { val id = _id } override def toString = "derived " + super.toString @@ -498,7 +494,7 @@ trait Init[Scope] { // This is intended for internal sbt use only, where alternatives like Plugin.globalSettings are not available. private[Init] sealed trait DefaultSetting[T] extends Setting[T] { val id: Long - override def make[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition): Setting[T] = super.make(key, init, pos) default id + override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = super.make(key, init, pos) default id override final def hashCode = id.hashCode override final def equals(o: Any): Boolean = o match { case d: DefaultSetting[_] => d.id == id; case _ => false } override def toString = s"default($id) " + super.toString @@ -547,7 +543,7 @@ trait Init[Scope] { case None => this case Some(const) => new Value(() => transform(const)) } - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init + private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = init } private[this] final class GetValue[S, T](val scopedKey: ScopedKey[S], val transform: S => T) extends Keyed[S, T] trait KeyedInitialize[T] extends Keyed[T, T] { @@ -585,7 +581,7 @@ trait Init[Scope] { new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn) } def mapConstant(g: MapConstant) = new Bind[S, T](s => f(s) mapConstant g, in mapConstant g) - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = in.processAttributes(init)(f) + private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = in.processAttributes(init)(f) } private[sbt] final class Optional[S, T](val a: Option[Initialize[S]], val f: Option[S] => T) extends Initialize[T] { def dependencies = deps(a.toList) @@ -599,7 +595,7 @@ trait Init[Scope] { def evaluate(ss: Settings[Scope]): T = f(a.flatMap(i => trapBadRef(evaluateT(ss)(i)))) // proper solution is for evaluate to be deprecated or for external use only and a new internal method returning Either be used private[this] def trapBadRef[A](run: => A): Option[A] = try Some(run) catch { case e: InvalidReference => None } - private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = a match { + private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = a match { case None => init case Some(i) => i.processAttributes(init)(f) } @@ -633,7 +629,7 @@ trait Init[Scope] { { val tx = alist.transform(inputs, validateKeyReferencedT(g)) val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten) - val get = new (ValidatedInit ~> Initialize) { def apply[T](vr: ValidatedInit[T]) = vr.right.get } + val get = new (ValidatedInit ~> Initialize) { def apply[B](vr: ValidatedInit[B]) = vr.right.get } if (undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs) } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala index 8631fc75b..0c9fac038 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala @@ -31,7 +31,7 @@ object Signals { import sun.misc.{ Signal, SignalHandler } val intSignal = new Signal(signal) val newHandler = new SignalHandler { - def handle(sig: Signal) { handler() } + def handle(sig: Signal): Unit = { handler() } } val oldHandler = Signal.handle(intSignal, newHandler) object unregisterNewHandler extends Registration { @@ -74,7 +74,7 @@ private final class Signals0 { import sun.misc.{ Signal, SignalHandler } val intSignal = new Signal(signal) val newHandler = new SignalHandler { - def handle(sig: Signal) { handler() } + def handle(sig: Signal): Unit = { handler() } } val oldHandler = Signal.handle(intSignal, newHandler) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index 712071684..23938b03d 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -52,7 +52,7 @@ abstract class JLine extends LineReader { } } - private[this] def resume() { + private[this] def resume(): Unit = { jline.TerminalFactory.reset JLine.terminal.init reader.drawLine() diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala index 350c58dfa..d5a96836e 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala @@ -7,7 +7,7 @@ package complete import History.number import java.io.File -final class History private (val lines: IndexedSeq[String], val path: Option[File], error: String => Unit) extends NotNull { +final class History private (val lines: IndexedSeq[String], val path: Option[File], error: String => Unit) { private def reversed = lines.reverse def all: Seq[String] = lines From d4116b03f47fc7fd049415ff5a6f7925d4adb57d Mon Sep 17 00:00:00 2001 From: eugene yokota Date: Sat, 23 Apr 2016 23:56:25 -0400 Subject: [PATCH 23/50] Fixes #2480. Workaround for Jline regression (#2570) Workaround jline/jline2#205 --- .../src/main/scala/sbt/internal/util/LineReader.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index 23938b03d..a345099fd 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -48,7 +48,10 @@ abstract class JLine extends LineReader { val lines = """\r?\n""".r.split(prompt) lines.length match { case 0 | 1 => prompt - case _ => reader.print(lines.init.mkString("\n") + "\n"); lines.last; + case _ => + // Workaround for regression jline/jline2#205 + reader.getOutput.write(lines.init.mkString("\n") + "\n") + lines.last } } From dca7a109ac70726e6f2dab9d96ec5a65add0de9d Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Mon, 22 Feb 2016 12:12:32 +0100 Subject: [PATCH 24/50] Completion for build-level keys sbt's shell provided completion only for keys that were relative to a defined project, but didn't provide completion for keys that belong to the build definition only. This commit fixes this issue by defining a new kind of `Parser` (from which completions are generated) which runs its input simultaneously on distinct parsers. We now define a parser for project-level keys and another parser for build-level keys. These two parsers are eventually combined, and we get the completions of both parsers. Fixes sbt/sbt#2460 --- .../sbt/internal/util/complete/Parser.scala | 28 +++++++++++++++++++ .../src/test/scala/ParserTest.scala | 9 ++++++ 2 files changed, 37 insertions(+) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index a6d5474aa..2642ad486 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -127,6 +127,9 @@ sealed trait RichParser[A] { /** Applies the original parser, applies `f` to the result to get the next parser, and applies that parser and uses its result for the overall result. */ def flatMap[B](f: A => Parser[B]): Parser[B] + + /** Applied both the original parser and `b` on the same input and returns the results produced by each parser */ + def combinedWith(b: Parser[A]): Parser[Seq[A]] } /** Contains Parser implementation helper methods not typically needed for using parsers. */ @@ -230,6 +233,12 @@ object Parser extends ParserMain { } } + def combinedParser[A](a: Parser[A], b: Parser[A]): Parser[Seq[A]] = + if (a.valid) + if (b.valid) new CombiningParser(a, b) else a.map(Seq(_)) + else + b.map(Seq(_)) + def choiceParser[A, B](a: Parser[A], b: Parser[B]): Parser[Either[A, B]] = if (a.valid) if (b.valid) new HetParser(a, b) else a.map(left.fn) @@ -310,6 +319,7 @@ trait ParserMain { def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg) def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString) def flatMap[B](f: A => Parser[B]) = bindParser(a, f) + def combinedWith(b: Parser[A]): Parser[Seq[A]] = combinedParser(a, b) } implicit def literalRichCharParser(c: Char): RichParser[Char] = richParser(c) @@ -633,6 +643,24 @@ private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidPar def completions(level: Int) = a.completions(level) ++ b.completions(level) override def toString = "(" + a + " || " + b + ")" } +private final class CombiningParser[T](a: Parser[T], b: Parser[T]) extends ValidParser[Seq[T]] { + lazy val result: Option[Seq[T]] = (a.result.toSeq ++ b.result.toSeq) match { case Seq() => None; case seq => Some(seq) } + def completions(level: Int) = a.completions(level) ++ b.completions(level) + def derive(i: Char) = + (a.valid, b.valid) match { + case (true, true) => new CombiningParser(a derive i, b derive i) + case (true, false) => a derive i map (Seq(_)) + case (false, true) => b derive i map (Seq(_)) + case (false, false) => new Invalid(mkFailure("No valid parser available.")) + } + def resultEmpty = + (a.resultEmpty, b.resultEmpty) match { + case (Value(ra), Value(rb)) => Value(Seq(ra, rb)) + case (Value(ra), _) => Value(Seq(ra)) + case (_, Value(rb)) => Value(Seq(rb)) + case _ => Value(Nil) + } +} private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) extends ValidParser[Seq[T]] { assert(a.nonEmpty) lazy val resultEmpty: Result[Seq[T]] = diff --git a/internal/util-complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala index 1db99b513..34e35efbe 100644 --- a/internal/util-complete/src/test/scala/ParserTest.scala +++ b/internal/util-complete/src/test/scala/ParserTest.scala @@ -108,6 +108,15 @@ object ParserTest extends Properties("Completing Parser") { property("repeatDep requires at least one token") = !matches(repeat, "") property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head) property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) + property("combined parser gives completion of both parsers") = { + val prefix = "fix" + val p1Suffixes = Set("", "ated", "ation") + val p2Suffixes = Set("es", "ed") + val p1: Parser[String] = p1Suffixes map (suffix => (prefix + suffix): Parser[String]) reduce (_ | _) + val p2: Parser[String] = p2Suffixes map (suffix => (prefix + suffix): Parser[String]) reduce (_ | _) + val suggestions: Set[Completion] = p1Suffixes ++ p2Suffixes map (new Suggestion(_)) + checkAll(prefix, p1 combinedWith p2, Completions(suggestions)) + } } object ParserExample { val ws = charClass(_.isWhitespace).+ From 27dee66399443636f72d01be6839b2fc68016378 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Tue, 23 Feb 2016 15:19:04 +0100 Subject: [PATCH 25/50] Address problems reported by Codacy --- .../scala/sbt/internal/util/complete/Parser.scala | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index 2642ad486..bc1229ad1 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -233,12 +233,6 @@ object Parser extends ParserMain { } } - def combinedParser[A](a: Parser[A], b: Parser[A]): Parser[Seq[A]] = - if (a.valid) - if (b.valid) new CombiningParser(a, b) else a.map(Seq(_)) - else - b.map(Seq(_)) - def choiceParser[A, B](a: Parser[A], b: Parser[B]): Parser[Either[A, B]] = if (a.valid) if (b.valid) new HetParser(a, b) else a.map(left.fn) @@ -319,7 +313,11 @@ trait ParserMain { def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg) def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString) def flatMap[B](f: A => Parser[B]) = bindParser(a, f) - def combinedWith(b: Parser[A]): Parser[Seq[A]] = combinedParser(a, b) + def combinedWith(b: Parser[A]): Parser[Seq[A]] = + if (a.valid) + if (b.valid) new CombiningParser(a, b) else a.map(Seq(_)) + else + b.map(Seq(_)) } implicit def literalRichCharParser(c: Char): RichParser[Char] = richParser(c) From b64e505746fe8569567bb9274b70727192c7d371 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 4 May 2016 16:27:29 -0400 Subject: [PATCH 26/50] Update dependencies --- .../src/test/scala/DagSpecification.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/util-collection/src/test/scala/DagSpecification.scala b/internal/util-collection/src/test/scala/DagSpecification.scala index 9e5025488..3b3614e39 100644 --- a/internal/util-collection/src/test/scala/DagSpecification.scala +++ b/internal/util-collection/src/test/scala/DagSpecification.scala @@ -19,8 +19,11 @@ object DagSpecification extends Properties("Dag") { val nodes = new HashSet[TestDag] def nonterminalGen(p: Gen.Parameters): Gen[TestDag] = { - for (i <- 0 until nodeCount; nextDeps <- Gen.someOf(nodes).apply(p)) - nodes += new TestDag(i, nextDeps) + val seed = rng.Seed.random() + for { + i <- 0 until nodeCount + nextDeps <- Gen.someOf(nodes).apply(p, seed) + } nodes += new TestDag(i, nextDeps) for (nextDeps <- Gen.someOf(nodes)) yield new TestDag(nodeCount, nextDeps) } Gen.parameterized(nonterminalGen) @@ -47,4 +50,4 @@ object DagSpecification extends Properties("Dag") { } class TestDag(id: Int, val dependencies: Iterable[TestDag]) extends Dag[TestDag] { override def toString = id + "->" + dependencies.mkString("[", ",", "]") -} \ No newline at end of file +} From 5f2e56a5a8ed62d8601605ea37111392356b651d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 19 Jun 2016 11:14:12 +0100 Subject: [PATCH 27/50] Fix compilation warnings, migrate to blackbox.Context --- .../src/test/scala/sbt/complete/FileExamplesTest.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index 2af9388a7..9cb416840 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -54,6 +54,7 @@ class FileExamplesTest extends UnitSpec { } } + // TODO: Remove DelayedInit - https://github.com/scala/scala/releases/tag/v2.11.0-RC1 class DirectoryStructure(withCompletionPrefix: String = "") extends DelayedInit { var fileExamples: FileExamples = _ var baseDir: File = _ From 8cc4a54c40eec7f4ee5907cc970b9c8a6dcc26ce Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 19 Jun 2016 11:42:31 +0100 Subject: [PATCH 28/50] Add -Ywarn-unused & -Ywarn-unused-import, & fix warnings --- .../src/main/scala/sbt/internal/util/Dag.scala | 1 - .../src/main/scala/sbt/internal/util/INode.scala | 2 +- .../src/main/scala/sbt/internal/util/Param.scala | 2 -- .../src/main/scala/sbt/internal/util/Settings.scala | 6 ------ internal/util-collection/src/test/scala/LiteralTest.scala | 4 +--- .../util-collection/src/test/scala/SettingsExample.scala | 1 - .../src/main/scala/sbt/internal/util/LineReader.scala | 3 +-- .../scala/sbt/internal/util/complete/EditDistance.scala | 3 +-- .../scala/sbt/internal/util/complete/HistoryCommands.scala | 1 - .../scala/sbt/internal/util/complete/JLineCompletion.scala | 2 +- 10 files changed, 5 insertions(+), 20 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala index 5cad287da..1c9d93ea0 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala @@ -94,7 +94,6 @@ object Dag { */ private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] = { - import scala.annotation.tailrec import graph._ val finished = new mutable.HashSet[Node] val visited = new mutable.HashSet[Node] diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala index d7a15eee8..d85cadf3f 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala @@ -3,7 +3,7 @@ package sbt.internal.util import java.lang.Runnable import java.util.concurrent.{ atomic, Executor, LinkedBlockingQueue } import atomic.{ AtomicBoolean, AtomicInteger } -import Types.{ :+:, ConstK, Id } +import Types.{ ConstK, Id } object EvaluationState extends Enumeration { val New, Blocked, Ready, Calling, Evaluated = Value diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala index 68671c8ca..dbded9292 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Param.scala @@ -3,8 +3,6 @@ */ package sbt.internal.util -import Types._ - // Used to emulate ~> literals trait Param[A[_], B[_]] { type T diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index a85a3faa6..3bee6fb9c 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -278,7 +278,6 @@ trait Init[Scope] { def flattenLocals(compiled: CompiledMap): Map[ScopedKey[_], Flattened] = { - import collection.breakOut val locals = compiled flatMap { case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil } val ordered = Dag.topologicalSort(locals)(_.dependencies.flatMap(dep => if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil)) def flatten(cmap: Map[ScopedKey[_], Flattened], key: ScopedKey[_], deps: Iterable[ScopedKey[_]]): Flattened = @@ -340,11 +339,6 @@ trait Init[Scope] { derivsByDef.getOrElseUpdate(key, new Deriveds(key, new mutable.ListBuffer)).settings += s } - // sort derived settings so that dependencies come first - // this is necessary when verifying that a derived setting's dependencies exist - val ddeps = (d: Deriveds) => d.dependencies.flatMap(derivsByDef.get) - val sortedDerivs = Dag.topologicalSort(derivsByDef.values)(ddeps) - // index derived settings by triggering key. This maps a key to the list of settings potentially derived from it. val derivedBy = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived]] for (s <- derived; d <- s.triggeredBy) diff --git a/internal/util-collection/src/test/scala/LiteralTest.scala b/internal/util-collection/src/test/scala/LiteralTest.scala index b50d02632..9353a07bf 100644 --- a/internal/util-collection/src/test/scala/LiteralTest.scala +++ b/internal/util-collection/src/test/scala/LiteralTest.scala @@ -3,8 +3,6 @@ */ package sbt.internal.util -import Types._ - // compilation test object LiteralTest { def x[A[_], B[_]](f: A ~> B) = f @@ -14,4 +12,4 @@ object LiteralTest { val a: List[Int] = f(Some(3)) val b: List[String] = f(Some("aa")) -} \ No newline at end of file +} diff --git a/internal/util-collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala index 5dd408282..0dd910773 100644 --- a/internal/util-collection/src/test/scala/SettingsExample.scala +++ b/internal/util-collection/src/test/scala/SettingsExample.scala @@ -32,7 +32,6 @@ object SettingsExample extends Init[Scope] { object SettingsUsage { import SettingsExample._ - import Types._ // Define some keys val a = AttributeKey[Int]("a") diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index a345099fd..c05a7427d 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -5,9 +5,8 @@ package sbt.internal.util import jline.console.ConsoleReader import jline.console.history.{ FileHistory, MemoryHistory } -import java.io.{ File, InputStream, PrintWriter, FileInputStream, FileDescriptor, FilterInputStream } +import java.io.{ File, InputStream, FileInputStream, FileDescriptor, FilterInputStream } import complete.Parser -import java.util.concurrent.atomic.AtomicBoolean import scala.concurrent.duration.Duration import scala.annotation.tailrec diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala index 8cb617348..79f488554 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala @@ -23,7 +23,6 @@ object EditDistance { for (i <- 1 to n; s_i = s(i - 1); j <- 1 to m) { val t_j = t(j - 1) val cost = if (s_i == t_j) matchCost else if (lower(s_i) == lower(t_j)) caseCost else subCost - val tcost = if (s_i == t_j) matchCost else transposeCost val c1 = d(i - 1)(j) + deleteCost val c2 = d(i)(j - 1) + insertCost @@ -39,4 +38,4 @@ object EditDistance { d(n)(m) } -} \ No newline at end of file +} diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala index 350a36610..f18f1619f 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala @@ -4,7 +4,6 @@ package sbt.internal.util package complete -import java.io.File import sbt.io.IO object HistoryCommands { diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala index e098f59f6..0b8b50502 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala @@ -5,7 +5,7 @@ package sbt.internal.util package complete import jline.console.ConsoleReader -import jline.console.completer.{ CandidateListCompletionHandler, Completer, CompletionHandler } +import jline.console.completer.{ Completer, CompletionHandler } import scala.annotation.tailrec import collection.JavaConversions From c4e6caf0ed2787c2b6e2f5f2a4d983755b545cc9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 19 Jun 2016 21:21:08 +0100 Subject: [PATCH 29/50] Remove Attribute#rawLabel --- .../src/main/scala/sbt/internal/util/Attributes.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index 25cf298d7..33591506c 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -19,9 +19,6 @@ sealed trait AttributeKey[T] { /** The runtime evidence for `T` */ def manifest: Manifest[T] - @deprecated("Should only be used for compatibility during the transition from hyphenated labels to camelCase labels.", "0.13.0") - def rawLabel: String - /** The label is the identifier for the key and is camelCase by convention. */ def label: String @@ -73,7 +70,6 @@ object AttributeKey { private[this] def make[T](name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int)(implicit mf: Manifest[T]): AttributeKey[T] = new SharedAttributeKey[T] { def manifest = mf - def rawLabel = name val label = Util.hyphenToCamel(name) def description = description0 def extend = extend0 @@ -81,7 +77,6 @@ object AttributeKey { } private[sbt] def local[T](implicit mf: Manifest[T]): AttributeKey[T] = new AttributeKey[T] { def manifest = mf - def rawLabel = LocalLabel def label = LocalLabel def description = None def extend = Nil @@ -207,4 +202,4 @@ object Attributed { /** Associates an empty metadata map with `data`. */ def blank[T](data: T): Attributed[T] = Attributed(data)(AttributeMap.empty) -} \ No newline at end of file +} From 84af84c3017c722c6ba75a512eeada76b7504968 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 19 Jun 2016 23:16:59 +0100 Subject: [PATCH 30/50] Un-deprecate now-private methods --- .../src/main/scala/sbt/internal/util/Settings.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index 3bee6fb9c..ed23e377a 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -452,9 +452,7 @@ trait Init[Scope] { def settings = this :: Nil def definitive: Boolean = !init.dependencies.contains(key) def dependencies: Seq[ScopedKey[_]] = remove(init.dependencies, key) - @deprecated("Will be made private.", "0.13.2") def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos) - @deprecated("Will be made private.", "0.13.2") def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] = (init validateReferenced g).right.map(newI => make(key, newI, pos)) private[sbt] def validateKeyReferenced(g: ValidateKeyRef): Either[Seq[Undefined], Setting[T]] = @@ -462,7 +460,6 @@ trait Init[Scope] { def mapKey(g: MapScoped): Setting[T] = make(g(key), init, pos) def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = make(key, init(t => f(key, t)), pos) - @deprecated("Will be made private.", "0.13.2") def mapConstant(g: MapConstant): Setting[T] = make(key, init mapConstant g, pos) def withPos(pos: SourcePosition) = make(key, init, pos) def positionString: Option[String] = pos match { From 43215db815b82b68111170525aeb79645f89ca87 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 8 Aug 2016 01:11:33 -0400 Subject: [PATCH 31/50] Trying to make readline timeout --- .../scala/sbt/internal/util/LineReader.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index c05a7427d..eb838824f 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -140,6 +140,22 @@ private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration) exten Thread.sleep(poll.toMillis) read() } + + @tailrec + final override def read(b: Array[Byte]): Int = + if (is.available() != 0) is.read(b) + else { + Thread.sleep(poll.toMillis) + read(b) + } + + @tailrec + final override def read(b: Array[Byte], off: Int, len: Int): Int = + if (is.available() != 0) is.read(b, off, len) + else { + Thread.sleep(poll.toMillis) + read(b, off, len) + } } trait LineReader { From d059b2f486c6583949e4201a1a053a83e9a9b736 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 12 Sep 2016 23:00:16 -0400 Subject: [PATCH 32/50] Handle sleep interruption --- .../scala/sbt/internal/util/LineReader.scala | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index eb838824f..b4d5d5f83 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -11,9 +11,10 @@ import scala.concurrent.duration.Duration import scala.annotation.tailrec abstract class JLine extends LineReader { - protected[this] val handleCONT: Boolean - protected[this] val reader: ConsoleReader - + protected[this] def handleCONT: Boolean + protected[this] def reader: ConsoleReader + protected[this] def injectThreadSleep: Boolean + protected[this] val in: InputStream = JLine.makeInputStream(injectThreadSleep) def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) } private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]): Option[String] = @@ -37,9 +38,13 @@ abstract class JLine extends LineReader { private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] = { val newprompt = handleMultilinePrompt(prompt) - mask match { - case Some(m) => Option(reader.readLine(newprompt, m)) - case None => Option(reader.readLine(newprompt)) + try { + mask match { + case Some(m) => Option(reader.readLine(newprompt, m)) + case None => Option(reader.readLine(newprompt)) + } + } catch { + case e: InterruptedException => Option("") } } @@ -81,6 +86,11 @@ private[sbt] object JLine { () } + protected[this] val originalIn = new FileInputStream(FileDescriptor.in) + private[sbt] def makeInputStream(injectThreadSleep: Boolean): InputStream = + if (injectThreadSleep) new InputStreamWrapper(originalIn, Duration("50 ms")) + else originalIn + // When calling this, ensure that enableEcho has been or will be called. // TerminalFactory.get will initialize the terminal to disable echo. private def terminal = jline.TerminalFactory.get @@ -98,14 +108,10 @@ private[sbt] object JLine { t.restore f(t) } - def createReader(): ConsoleReader = createReader(None, true) - def createReader(historyPath: Option[File], injectThreadSleep: Boolean): ConsoleReader = + def createReader(): ConsoleReader = createReader(None, JLine.makeInputStream(true)) + def createReader(historyPath: Option[File], in: InputStream): ConsoleReader = usingTerminal { t => - val cr = if (injectThreadSleep) { - val originalIn = new FileInputStream(FileDescriptor.in) - val in = new InputStreamWrapper(originalIn, Duration("50 ms")) - new ConsoleReader(in, System.out) - } else new ConsoleReader + val cr = new ConsoleReader(in, System.out) cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650 cr.setBellEnabled(false) val h = historyPath match { @@ -169,14 +175,15 @@ final class FullReader( ) extends JLine { protected[this] val reader = { - val cr = JLine.createReader(historyPath, injectThreadSleep) + val cr = JLine.createReader(historyPath, in) sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete) cr } } class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean, val injectThreadSleep: Boolean) extends JLine { - protected[this] val reader = JLine.createReader(historyPath, injectThreadSleep) + protected[this] val reader = JLine.createReader(historyPath, in) + } object SimpleReader extends SimpleReader(None, JLine.HandleCONT, false) From f6e4c7c85ec73058cadd294ee75ddc3c8dffdc8b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 27 Oct 2016 11:13:45 +0100 Subject: [PATCH 33/50] Expose Eval. Fixes sbt/sbt#2616 --- .../src/main/scala/sbt/{internal => }/util/Eval.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/util-collection/src/main/scala/sbt/{internal => }/util/Eval.scala (100%) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala b/internal/util-collection/src/main/scala/sbt/util/Eval.scala similarity index 100% rename from internal/util-collection/src/main/scala/sbt/internal/util/Eval.scala rename to internal/util-collection/src/main/scala/sbt/util/Eval.scala From 042d3de956a7118eb965c73ba84b341d672670a6 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 28 Oct 2016 12:19:41 +0100 Subject: [PATCH 34/50] Really expose Eval. Fixes error in #50 --- internal/util-collection/src/main/scala/sbt/util/Eval.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/util-collection/src/main/scala/sbt/util/Eval.scala b/internal/util-collection/src/main/scala/sbt/util/Eval.scala index 1596e457b..8c142e336 100644 --- a/internal/util-collection/src/main/scala/sbt/util/Eval.scala +++ b/internal/util-collection/src/main/scala/sbt/util/Eval.scala @@ -1,4 +1,4 @@ -package sbt.internal.util +package sbt.util import scala.annotation.tailrec From 1526bf4f853918a3ad8e7a08f935dd1918c88e13 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 15 Nov 2016 17:14:10 +0000 Subject: [PATCH 35/50] Remove deprecated methods --- .../scala/sbt/internal/util/Settings.scala | 23 +++-------------- .../main/scala/sbt/internal/util/Util.scala | 14 +++++------ .../internal/util/complete/Completions.scala | 10 +------- .../sbt/internal/util/complete/Parser.scala | 25 ------------------- 4 files changed, 10 insertions(+), 62 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index ed23e377a..97117e2dc 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -87,9 +87,6 @@ trait Init[Scope] { */ private[sbt] final def validated[T](key: ScopedKey[T], selfRefOk: Boolean): ValidationCapture[T] = new ValidationCapture(key, selfRefOk) - @deprecated("Use the version with default arguments and default parameter.", "0.13.7") - final def derive[T](s: Setting[T], allowDynamic: Boolean, filter: Scope => Boolean, trigger: AttributeKey[_] => Boolean): Setting[T] = - derive(s, allowDynamic, filter, trigger, false) /** * Constructs a derived setting that will be automatically defined in every scope where one of its dependencies * is explicitly defined and the where the scope matches `filter`. @@ -101,10 +98,12 @@ trait Init[Scope] { val d = new DerivedSetting[T](s.key, s.init, s.pos, filter, trigger) if (default) d.default() else d } + def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] = s.init match { case _: Bind[_, _] if !allowDynamic => Some("Cannot derive from dynamic dependencies.") case _ => None } + // id is used for equality private[sbt] final def defaultSetting[T](s: Setting[T]): Setting[T] = s.default() private[sbt] def defaultSettings(ss: Seq[Setting[_]]): Seq[Setting[_]] = ss.map(s => defaultSetting(s)) @@ -238,14 +237,7 @@ trait Init[Scope] { } final class Uninitialized(val undefined: Seq[Undefined], override val toString: String) extends Exception(toString) - final class Undefined private[sbt] (val defining: Setting[_], val referencedKey: ScopedKey[_]) { - @deprecated("For compatibility only, use `defining` directly.", "0.13.1") - val definingKey = defining.key - @deprecated("For compatibility only, use `defining` directly.", "0.13.1") - val derived: Boolean = defining.isDerived - @deprecated("Use the non-deprecated Undefined factory method.", "0.13.1") - def this(definingKey: ScopedKey[_], referencedKey: ScopedKey[_], derived: Boolean) = this(fakeUndefinedSetting(definingKey, derived), referencedKey) - } + final class Undefined private[sbt] (val defining: Setting[_], val referencedKey: ScopedKey[_]) final class RuntimeUndefined(val undefined: Seq[Undefined]) extends RuntimeException("References to undefined settings at runtime.") { override def getMessage = super.getMessage + undefined.map { u => @@ -253,15 +245,6 @@ trait Init[Scope] { }.mkString } - @deprecated("Use the other overload.", "0.13.1") - def Undefined(definingKey: ScopedKey[_], referencedKey: ScopedKey[_], derived: Boolean): Undefined = - new Undefined(fakeUndefinedSetting(definingKey, derived), referencedKey) - private[this] def fakeUndefinedSetting[T](definingKey: ScopedKey[T], d: Boolean): Setting[T] = - { - val init: Initialize[T] = pure(() => sys.error("Dummy setting for compatibility only.")) - new Setting(definingKey, init, NoPosition) { override def isDerived = d } - } - def Undefined(defining: Setting[_], referencedKey: ScopedKey[_]): Undefined = new Undefined(defining, referencedKey) def Uninitialized(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], keys: Seq[Undefined], runtime: Boolean)(implicit display: Show[ScopedKey[_]]): Uninitialized = { diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala index 4f82cae9c..75b8224c1 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala @@ -25,18 +25,16 @@ object Util { def pairID[A, B] = (a: A, b: B) => (a, b) - private[this] lazy val Hypen = """-(\p{javaLowerCase})""".r + private[this] lazy val Hyphen = """-(\p{javaLowerCase})""".r + def hasHyphen(s: String): Boolean = s.indexOf('-') >= 0 - @deprecated("Use the properly spelled version: hyphenToCamel", "0.13.0") - def hypenToCamel(s: String): String = hyphenToCamel(s) + def hyphenToCamel(s: String): String = - if (hasHyphen(s)) - Hypen.replaceAllIn(s, _.group(1).toUpperCase(Locale.ENGLISH)) - else - s + if (hasHyphen(s)) Hyphen.replaceAllIn(s, _.group(1).toUpperCase(Locale.ENGLISH)) else s private[this] lazy val Camel = """(\p{javaLowerCase})(\p{javaUpperCase})""".r - def camelToHypen(s: String): String = + + def camelToHyphen(s: String): String = Camel.replaceAllIn(s, m => m.group(1) + "-" + m.group(2).toLowerCase(Locale.ENGLISH)) def quoteIfKeyword(s: String): String = if (ScalaKeywords.values(s)) '`' + s + '`' else s diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala index c035f3620..47dbb3b4f 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala @@ -82,8 +82,6 @@ final class DisplayOnly(val display: String) extends Completion { override def toString = "{" + display + "}" } final class Token(val display: String, val append: String) extends Completion { - @deprecated("Retained only for compatibility. All information is now in `display` and `append`.", "0.12.1") - lazy val prepend = display.stripSuffix(append) def isEmpty = display.isEmpty && append.isEmpty override final def toString = "[" + display + "]++" + append } @@ -127,19 +125,13 @@ object Completion { // TODO: make strict in 0.13.0 to match DisplayOnly def displayOnly(value: => String): Completion = new DisplayOnly(value) - @deprecated("Use displayOnly.", "0.12.1") - def displayStrict(value: String): Completion = displayOnly(value) // TODO: make strict in 0.13.0 to match Token def token(prepend: => String, append: => String): Completion = new Token(prepend + append, append) - @deprecated("Use token.", "0.12.1") - def tokenStrict(prepend: String, append: String): Completion = token(prepend, append) /** @since 0.12.1 */ def tokenDisplay(append: String, display: String): Completion = new Token(display, append) // TODO: make strict in 0.13.0 to match Suggestion def suggestion(value: => String): Completion = new Suggestion(value) - @deprecated("Use suggestion.", "0.12.1") - def suggestStrict(value: String): Completion = suggestion(value) -} \ No newline at end of file +} diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index 0e12fd28a..67651bfbd 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -79,18 +79,12 @@ sealed trait RichParser[A] { */ def failOnException: Parser[A] - @deprecated("Use `not` and explicitly provide the failure message", "0.12.2") - def unary_- : Parser[Unit] - /** * Apply the original parser, but only succeed if `o` also succeeds. * Note that `o` does not need to consume the same amount of input to satisfy this condition. */ def &(o: Parser[_]): Parser[A] - @deprecated("Use `and` and `not` and explicitly provide the failure message", "0.12.2") - def -(o: Parser[_]): Parser[A] - /** Explicitly defines the completions for the original Parser.*/ def examples(s: String*): Parser[A] @@ -188,12 +182,6 @@ object Parser extends ParserMain { def mkFailures(errors: => Seq[String], definitive: Boolean = false): Failure = new Failure(errors.distinct, definitive) def mkFailure(error: => String, definitive: Boolean = false): Failure = new Failure(error :: Nil, definitive) - @deprecated("This method is deprecated and will be removed in the next major version. Use the parser directly to check for invalid completions.", since = "0.13.2") - def checkMatches(a: Parser[_], completions: Seq[String]): Unit = { - val bad = completions.filter(apply(a)(_).resultEmpty.isFailure) - if (bad.nonEmpty) sys.error("Invalid example completions: " + bad.mkString("'", "', '", "'")) - } - def tuple[A, B](a: Option[A], b: Option[B]): Option[(A, B)] = (a, b) match { case (Some(av), Some(bv)) => Some((av, bv)); case _ => None } @@ -280,9 +268,6 @@ object Parser extends ParserMain { } } - @deprecated("Explicitly call `and` and `not` to provide the failure message.", "0.12.2") - def sub[T](a: Parser[T], b: Parser[_]): Parser[T] = and(a, not(b)) - def and[T](a: Parser[T], b: Parser[_]): Parser[T] = a.ifValid(b.ifValid(new And(a, b))) } trait ParserMain { @@ -527,13 +512,6 @@ trait ParserMain { def token[T](t: Parser[T], complete: TokenCompletions): Parser[T] = mkToken(t, "", complete) - @deprecated("Use a different `token` overload.", "0.12.1") - def token[T](t: Parser[T], seen: String, track: Boolean, hide: Int => Boolean): Parser[T] = - { - val base = if (track) TokenCompletions.default else TokenCompletions.displayOnly(seen) - token(t, base.hideWhen(hide)) - } - private[sbt] def mkToken[T](t: Parser[T], seen: String, complete: TokenCompletions): Parser[T] = if (t.valid && !t.isTokenStart) if (t.result.isEmpty) new TokenStart(t, seen, complete) else t @@ -547,9 +525,6 @@ trait ParserMain { case (av, bv) => new HomParser(a, b) } - @deprecated("Explicitly specify the failure message.", "0.12.2") - def not(p: Parser[_]): Parser[Unit] = not(p, "Excluded.") - def not(p: Parser[_], failMessage: String): Parser[Unit] = p.result match { case None => new Not(p, failMessage) case Some(_) => failure(failMessage) From 95d52077dbd0a54decb98fffb98c2ab1d53636e7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 31 Oct 2016 09:23:38 +0000 Subject: [PATCH 36/50] Lazily concatenate failed errors for completion [forwardport] (#53) * Fixes [sbt/sbt#2781] * When using `` completion the failed errors were always computed for mathcing projects even if there was no failure, leading to excessive computation of Levenshtein distances and a large lag (seconds) on builds with many matching projects. --- .../src/main/scala/sbt/internal/util/complete/Parser.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index bc1229ad1..0e12fd28a 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -559,8 +559,8 @@ trait ParserMain { def seq[T](p: Seq[Parser[T]]): Parser[Seq[T]] = seq0(p, Nil) def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] = { - val (newErrors, valid) = separate(p) { case Invalid(f) => Left(f.errors); case ok => Right(ok) } - def combinedErrors = errors ++ newErrors.flatten + val (newErrors, valid) = separate(p) { case Invalid(f) => Left(f.errors _); case ok => Right(ok) } + def combinedErrors = errors ++ newErrors.flatMap(_()) if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors) } From 58be28e39909b4eac2767fbb1a8221f019157d2d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 22 Dec 2016 11:50:34 -0500 Subject: [PATCH 37/50] Update Eval https://github.com/typelevel/cats/blob/e2335730a958ce605f0d75f1b0d838454 2336aaf/core/src/main/scala/cats/Eval.scala --- .../src/main/scala/sbt/util/Eval.scala | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/util/Eval.scala b/internal/util-collection/src/main/scala/sbt/util/Eval.scala index 8c142e336..abfb38070 100644 --- a/internal/util-collection/src/main/scala/sbt/util/Eval.scala +++ b/internal/util-collection/src/main/scala/sbt/util/Eval.scala @@ -5,9 +5,37 @@ import scala.annotation.tailrec // Copied from Cats (MIT license) /** - * Eval is a datatype, which controls evaluation. + * Eval is a monad which controls evaluation. + * + * This type wraps a value (or a computation that produces a value) + * and can produce it on command via the `.value` method. + * + * There are three basic evaluation strategies: + * + * - Now: evaluated immediately + * - Later: evaluated once when value is needed + * - Always: evaluated every time value is needed + * + * The Later and Always are both lazy strategies while Now is eager. + * Later and Always are distinguished from each other only by + * memoization: once evaluated Later will save the value to be returned + * immediately if it is needed again. Always will run its computation + * every time. + * + * Eval supports stack-safe lazy computation via the .map and .flatMap + * methods, which use an internal trampoline to avoid stack overflows. + * Computation done within .map and .flatMap is always done lazily, + * even when applied to a Now instance. + * + * It is not generally good style to pattern-match on Eval instances. + * Rather, use .map and .flatMap to chain computation, and use .value + * to get the result when needed. It is also not good style to create + * Eval instances whose computation involves calling .value on another + * Eval instance -- this can defeat the trampolining and lead to stack + * overflows. */ -sealed abstract class Eval[A] extends Serializable { self => +sealed abstract class Eval[+A] extends Serializable { self => + /** * Evaluate the computation and return an A value. * @@ -15,7 +43,7 @@ sealed abstract class Eval[A] extends Serializable { self => * will be performed at this point. For eager instances (Now), a * value will be immediately returned. */ - def get: A + def value: A /** * Transform an Eval[A] into an Eval[B] given the transformation @@ -47,8 +75,11 @@ sealed abstract class Eval[A] extends Serializable { self => case c: Eval.Compute[A] => new Eval.Compute[B] { type Start = c.Start - val start = c.start - val run = (s: c.Start) => + // See https://issues.scala-lang.org/browse/SI-9931 for an explanation + // of why the type annotations are necessary in these two lines on + // Scala 2.12.0. + val start: () => Eval[Start] = c.start + val run: Start => Eval[B] = (s: c.Start) => new Eval.Compute[B] { type Start = A val start = () => c.run(s) @@ -87,7 +118,7 @@ sealed abstract class Eval[A] extends Serializable { self => * This type should be used when an A value is already in hand, or * when the computation to produce an A value is pure and very fast. */ -final case class Now[A](get: A) extends Eval[A] { +final case class Now[A](value: A) extends Eval[A] { def memoize: Eval[A] = this } @@ -115,7 +146,7 @@ final class Later[A](f: () => A) extends Eval[A] { // // (For situations where `f` is small, but the output will be very // expensive to store, consider using `Always`.) - lazy val get: A = { + lazy val value: A = { val result = thunk() thunk = null // scalastyle:off result @@ -139,7 +170,7 @@ object Later { * caching must be avoided. Generally, prefer Later. */ final class Always[A](f: () => A) extends Eval[A] { - def get: A = f() + def value: A = f() def memoize: Eval[A] = new Later(f) } @@ -193,8 +224,8 @@ object Eval { * they will be automatically created when needed. */ sealed abstract class Call[A](val thunk: () => Eval[A]) extends Eval[A] { - def memoize: Eval[A] = new Later(() => get) - def get: A = Call.loop(this).get + def memoize: Eval[A] = new Later(() => value) + def value: A = Call.loop(this).value } object Call { @@ -229,16 +260,16 @@ object Eval { * * Unlike a traditional trampoline, the internal workings of the * trampoline are not exposed. This allows a slightly more efficient - * implementation of the .get method. + * implementation of the .value method. */ sealed abstract class Compute[A] extends Eval[A] { type Start val start: () => Eval[Start] val run: Start => Eval[A] - def memoize: Eval[A] = Later(get) + def memoize: Eval[A] = Later(value) - def get: A = { + def value: A = { type L = Eval[Any] type C = Any => Eval[Any] @tailrec def loop(curr: L, fs: List[C]): Any = @@ -251,12 +282,12 @@ object Eval { cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs ) case xx => - loop(c.run(xx.get).asInstanceOf[L], fs) + loop(c.run(xx.value), fs) } case x => fs match { - case f :: fs => loop(f(x.get), fs) - case Nil => x.get + case f :: fs => loop(f(x.value), fs) + case Nil => x.value } } loop(this.asInstanceOf[L], Nil).asInstanceOf[A] From cfc5efe798f42870e10855421ea803d8c4388165 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 22 Dec 2016 14:34:37 -0500 Subject: [PATCH 38/50] Work around Scala 2.12 init deadlock (SI-9824) --- .../util-collection/src/test/scala/SettingsExample.scala | 6 +++--- .../util-collection/src/test/scala/SettingsTest.scala | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/internal/util-collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala index 0dd910773..cf65d6c68 100644 --- a/internal/util-collection/src/test/scala/SettingsExample.scala +++ b/internal/util-collection/src/test/scala/SettingsExample.scala @@ -10,7 +10,7 @@ final case class Scope(nestIndex: Int, idAtIndex: Int = 0) // Lots of type constructors would become binary, which as you may know requires lots of type lambdas // when you want a type function with only one parameter. // That would be a general pain.) -object SettingsExample extends Init[Scope] { +case class SettingsExample() extends Init[Scope] { // Provides a way of showing a Scope+AttributeKey[_] val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] { def apply(key: ScopedKey[_]) = s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}" @@ -30,8 +30,8 @@ object SettingsExample extends Init[Scope] { /** Usage Example **/ -object SettingsUsage { - import SettingsExample._ +case class SettingsUsage(val settingsExample: SettingsExample) { + import settingsExample._ // Define some keys val a = AttributeKey[Int]("a") diff --git a/internal/util-collection/src/test/scala/SettingsTest.scala b/internal/util-collection/src/test/scala/SettingsTest.scala index 85a3760ee..3e0bf0c0f 100644 --- a/internal/util-collection/src/test/scala/SettingsTest.scala +++ b/internal/util-collection/src/test/scala/SettingsTest.scala @@ -2,10 +2,12 @@ package sbt.internal.util import org.scalacheck._ import Prop._ -import SettingsUsage._ -import SettingsExample._ object SettingsTest extends Properties("settings") { + val settingsExample: SettingsExample = SettingsExample() + import settingsExample._ + val settingsUsage = SettingsUsage(settingsExample) + import settingsUsage._ import scala.reflect.Manifest @@ -126,7 +128,7 @@ object SettingsTest extends Properties("settings") { // Each project defines an initial value, but the update is defined in globalKey. // However, the derived Settings that come from this should be scoped in each project. val settings: Seq[Setting[_]] = - derive(setting(globalDerivedKey, SettingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0))) + derive(setting(globalDerivedKey, settingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0))) val ev = evaluate(settings) // Also check that the key has no value at the "global" scope val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev) @@ -184,6 +186,7 @@ object SettingsTest extends Properties("settings") { } // This setup is a workaround for module synchronization issues final class CCR(intermediate: Int) { + import SettingsTest.settingsExample._ lazy val top = iterate(value(intermediate), intermediate) def iterate(init: Initialize[Int], i: Int): Initialize[Int] = bind(init) { t => From 1b19c504a448528fe2af0fd1ad38f8bdfa0b3238 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 16 Jan 2017 13:26:31 -0500 Subject: [PATCH 39/50] Revert "Merge pull request #41 from eed3si9n/wip/2469" This reverts commit 0da2f30ee8b895933c3fd88d2401fc82d1a3e01a, reversing changes made to 93418589b7a0839baec551a45f6525000af03d4d. --- .../sbt/internal/util/complete/Parser.scala | 26 ------------------- .../src/test/scala/ParserTest.scala | 9 ------- 2 files changed, 35 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index 67651bfbd..003862c5e 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -121,9 +121,6 @@ sealed trait RichParser[A] { /** Applies the original parser, applies `f` to the result to get the next parser, and applies that parser and uses its result for the overall result. */ def flatMap[B](f: A => Parser[B]): Parser[B] - - /** Applied both the original parser and `b` on the same input and returns the results produced by each parser */ - def combinedWith(b: Parser[A]): Parser[Seq[A]] } /** Contains Parser implementation helper methods not typically needed for using parsers. */ @@ -298,11 +295,6 @@ trait ParserMain { def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg) def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString) def flatMap[B](f: A => Parser[B]) = bindParser(a, f) - def combinedWith(b: Parser[A]): Parser[Seq[A]] = - if (a.valid) - if (b.valid) new CombiningParser(a, b) else a.map(Seq(_)) - else - b.map(Seq(_)) } implicit def literalRichCharParser(c: Char): RichParser[Char] = richParser(c) @@ -616,24 +608,6 @@ private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidPar def completions(level: Int) = a.completions(level) ++ b.completions(level) override def toString = "(" + a + " || " + b + ")" } -private final class CombiningParser[T](a: Parser[T], b: Parser[T]) extends ValidParser[Seq[T]] { - lazy val result: Option[Seq[T]] = (a.result.toSeq ++ b.result.toSeq) match { case Seq() => None; case seq => Some(seq) } - def completions(level: Int) = a.completions(level) ++ b.completions(level) - def derive(i: Char) = - (a.valid, b.valid) match { - case (true, true) => new CombiningParser(a derive i, b derive i) - case (true, false) => a derive i map (Seq(_)) - case (false, true) => b derive i map (Seq(_)) - case (false, false) => new Invalid(mkFailure("No valid parser available.")) - } - def resultEmpty = - (a.resultEmpty, b.resultEmpty) match { - case (Value(ra), Value(rb)) => Value(Seq(ra, rb)) - case (Value(ra), _) => Value(Seq(ra)) - case (_, Value(rb)) => Value(Seq(rb)) - case _ => Value(Nil) - } -} private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) extends ValidParser[Seq[T]] { assert(a.nonEmpty) lazy val resultEmpty: Result[Seq[T]] = diff --git a/internal/util-complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala index 34e35efbe..1db99b513 100644 --- a/internal/util-complete/src/test/scala/ParserTest.scala +++ b/internal/util-complete/src/test/scala/ParserTest.scala @@ -108,15 +108,6 @@ object ParserTest extends Properties("Completing Parser") { property("repeatDep requires at least one token") = !matches(repeat, "") property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head) property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) - property("combined parser gives completion of both parsers") = { - val prefix = "fix" - val p1Suffixes = Set("", "ated", "ation") - val p2Suffixes = Set("es", "ed") - val p1: Parser[String] = p1Suffixes map (suffix => (prefix + suffix): Parser[String]) reduce (_ | _) - val p2: Parser[String] = p2Suffixes map (suffix => (prefix + suffix): Parser[String]) reduce (_ | _) - val suggestions: Set[Completion] = p1Suffixes ++ p2Suffixes map (new Suggestion(_)) - checkAll(prefix, p1 combinedWith p2, Completions(suggestions)) - } } object ParserExample { val ws = charClass(_.isWhitespace).+ From 73b24681c597ff96fc2866d13df8dbd787c689f7 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 30 Jan 2017 19:20:06 -0500 Subject: [PATCH 40/50] Implements registerStringCodec Uses TypeTag to recover the full name of type parameter, which is calculated by StringTypeTag. This is sent along in ObjectEvent. On the other end, we can lookup typeclass instances using the tag key. --- .../src/main/scala/sbt/internal/util/INode.scala | 2 +- .../src/main/scala/sbt/internal/util/Settings.scala | 11 ++++++----- .../src/main/scala/sbt/internal/util/Show.scala | 8 -------- .../src/main/scala/sbt/util/Show.scala | 12 ++++++++++++ .../scala/sbt/{internal => }/util/ShowLines.scala | 2 +- .../src/test/scala/SettingsExample.scala | 9 ++++++--- 6 files changed, 26 insertions(+), 18 deletions(-) delete mode 100644 internal/util-collection/src/main/scala/sbt/internal/util/Show.scala create mode 100644 internal/util-collection/src/main/scala/sbt/util/Show.scala rename internal/util-collection/src/main/scala/sbt/{internal => }/util/ShowLines.scala (92%) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala index d85cadf3f..3c159ec11 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala @@ -95,7 +95,7 @@ abstract class EvaluateSettings[Scope] { keyString private[this] def keyString = - (static.toSeq.flatMap { case (key, value) => if (value eq this) init.showFullKey(key) :: Nil else Nil }).headOption getOrElse "non-static" + (static.toSeq.flatMap { case (key, value) => if (value eq this) init.showFullKey.show(key) :: Nil else Nil }).headOption getOrElse "non-static" final def get: T = synchronized { assert(value != null, toString + " not evaluated") diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index 97117e2dc..afa02c150 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -6,6 +6,7 @@ package sbt.internal.util import scala.language.existentials import Types._ +import sbt.util.Show sealed trait Settings[Scope] { def data: Map[Scope, AttributeMap] @@ -119,7 +120,7 @@ trait Init[Scope] { def mapScope(f: Scope => Scope): MapScoped = new MapScoped { def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope)) } - private final class InvalidReference(val key: ScopedKey[_]) extends RuntimeException("Internal settings error: invalid reference to " + showFullKey(key)) + private final class InvalidReference(val key: ScopedKey[_]) extends RuntimeException("Internal settings error: invalid reference to " + showFullKey.show(key)) private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] = { @@ -215,11 +216,11 @@ trait Init[Scope] { { val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey) val derived = u.defining.isDerived - val refString = display(u.defining.key) + val refString = display.show(u.defining.key) val sourceString = if (derived) "" else parenPosString(u.defining) - val guessedString = if (derived) "" else guessed.map(g => "\n Did you mean " + display(g) + " ?").toList.mkString + val guessedString = if (derived) "" else guessed.map(g => "\n Did you mean " + display.show(g) + " ?").toList.mkString val derivedString = if (derived) ", which is a derived setting that needs this key to be defined in this scope." else "" - display(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString + display.show(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString } private[this] def parenPosString(s: Setting[_]): String = s.positionString match { case None => ""; case Some(s) => " (" + s + ")" } @@ -255,7 +256,7 @@ trait Init[Scope] { new Uninitialized(keys, prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ") } final class Compiled[T](val key: ScopedKey[T], val dependencies: Iterable[ScopedKey[_]], val settings: Seq[Setting[T]]) { - override def toString = showFullKey(key) + override def toString = showFullKey.show(key) } final class Flattened(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]]) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Show.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Show.scala deleted file mode 100644 index 4a0343ed7..000000000 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Show.scala +++ /dev/null @@ -1,8 +0,0 @@ -package sbt.internal.util - -trait Show[T] { - def apply(t: T): String -} -object Show { - def apply[T](f: T => String): Show[T] = new Show[T] { def apply(t: T): String = f(t) } -} \ No newline at end of file diff --git a/internal/util-collection/src/main/scala/sbt/util/Show.scala b/internal/util-collection/src/main/scala/sbt/util/Show.scala new file mode 100644 index 000000000..20ac0565d --- /dev/null +++ b/internal/util-collection/src/main/scala/sbt/util/Show.scala @@ -0,0 +1,12 @@ +package sbt.util + +trait Show[A] { + def show(a: A): String +} +object Show { + def apply[A](f: A => String): Show[A] = new Show[A] { def show(a: A): String = f(a) } + + def fromToString[A]: Show[A] = new Show[A] { + def show(a: A): String = a.toString + } +} diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/ShowLines.scala b/internal/util-collection/src/main/scala/sbt/util/ShowLines.scala similarity index 92% rename from internal/util-collection/src/main/scala/sbt/internal/util/ShowLines.scala rename to internal/util-collection/src/main/scala/sbt/util/ShowLines.scala index f99a1394c..65729d747 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/ShowLines.scala +++ b/internal/util-collection/src/main/scala/sbt/util/ShowLines.scala @@ -1,4 +1,4 @@ -package sbt.internal.util +package sbt.util trait ShowLines[A] { def showLines(a: A): Seq[String] diff --git a/internal/util-collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala index cf65d6c68..20536a5ef 100644 --- a/internal/util-collection/src/test/scala/SettingsExample.scala +++ b/internal/util-collection/src/test/scala/SettingsExample.scala @@ -1,5 +1,7 @@ package sbt.internal.util +import sbt.util.Show + /** Define our settings system */ // A basic scope indexed by an integer. @@ -12,9 +14,10 @@ final case class Scope(nestIndex: Int, idAtIndex: Int = 0) // That would be a general pain.) case class SettingsExample() extends Init[Scope] { // Provides a way of showing a Scope+AttributeKey[_] - val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] { - def apply(key: ScopedKey[_]) = s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}" - } + val showFullKey: Show[ScopedKey[_]] = Show[ScopedKey[_]]((key: ScopedKey[_]) => + { + s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}" + }) // A sample delegation function that delegates to a Scope with a lower index. val delegates: Scope => Seq[Scope] = { From ae4e53ceb0fc56d9787fc1a70b71580d330c9437 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 21 Mar 2017 00:09:07 +0000 Subject: [PATCH 41/50] Define OptJsonWriter & put it on AttributeKey --- .../scala/sbt/internal/util/Attributes.scala | 20 +++++++++++-------- .../sbt/internal/util/OptJsonWriter.scala | 14 +++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 internal/util-collection/src/main/scala/sbt/internal/util/OptJsonWriter.scala diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index 33591506c..b8ed058a1 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -39,6 +39,8 @@ sealed trait AttributeKey[T] { /** Identifies the relative importance of a key among other keys.*/ def rank: Int + + def optJsonWriter: OptJsonWriter[T] } private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] { override final def toString = label @@ -50,32 +52,33 @@ private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] { final def isLocal: Boolean = false } object AttributeKey { - def apply[T](name: String)(implicit mf: Manifest[T]): AttributeKey[T] = + def apply[T](name: String)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = make(name, None, Nil, Int.MaxValue) - def apply[T](name: String, rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = + def apply[T](name: String, rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = make(name, None, Nil, rank) - def apply[T](name: String, description: String)(implicit mf: Manifest[T]): AttributeKey[T] = + def apply[T](name: String, description: String)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = apply(name, description, Nil) - def apply[T](name: String, description: String, rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = + def apply[T](name: String, description: String, rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = apply(name, description, Nil, rank) - def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]])(implicit mf: Manifest[T]): AttributeKey[T] = + def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]])(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = apply(name, description, extend, Int.MaxValue) - def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]], rank: Int)(implicit mf: Manifest[T]): AttributeKey[T] = + def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]], rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = make(name, Some(description), extend, rank) - private[this] def make[T](name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int)(implicit mf: Manifest[T]): AttributeKey[T] = new SharedAttributeKey[T] { + private[this] def make[T](name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = new SharedAttributeKey[T] { def manifest = mf val label = Util.hyphenToCamel(name) def description = description0 def extend = extend0 def rank = rank0 + def optJsonWriter = ojw } - private[sbt] def local[T](implicit mf: Manifest[T]): AttributeKey[T] = new AttributeKey[T] { + private[sbt] def local[T](implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = new AttributeKey[T] { def manifest = mf def label = LocalLabel def description = None @@ -83,6 +86,7 @@ object AttributeKey { override def toString = label def isLocal: Boolean = true def rank = Int.MaxValue + val optJsonWriter = ojw } private[sbt] final val LocalLabel = "$" + "local" } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/OptJsonWriter.scala b/internal/util-collection/src/main/scala/sbt/internal/util/OptJsonWriter.scala new file mode 100644 index 000000000..f44ef44e8 --- /dev/null +++ b/internal/util-collection/src/main/scala/sbt/internal/util/OptJsonWriter.scala @@ -0,0 +1,14 @@ +package sbt.internal.util + +import sjsonnew.JsonWriter + +sealed trait OptJsonWriter[A] +final case class NoJsonWriter[A]() extends OptJsonWriter[A] +final case class SomeJsonWriter[A](value: JsonWriter[A]) extends OptJsonWriter[A] + +trait OptJsonWriter0 { + implicit def fallback[A]: NoJsonWriter[A] = NoJsonWriter() +} +object OptJsonWriter extends OptJsonWriter0 { + implicit def lift[A](implicit z: JsonWriter[A]): SomeJsonWriter[A] = SomeJsonWriter(z) +} From 9cfa17d09e3692120735649940fd53db684e2ffe Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 21 Mar 2017 10:46:58 +0000 Subject: [PATCH 42/50] Move OptJsonWriter to public API --- .../src/main/scala/sbt/internal/util/Attributes.scala | 1 + .../src/main/scala/sbt/{internal => }/util/OptJsonWriter.scala | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename internal/util-collection/src/main/scala/sbt/{internal => }/util/OptJsonWriter.scala (94%) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index b8ed058a1..5cf6fb65b 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -5,6 +5,7 @@ package sbt.internal.util import Types._ import scala.reflect.Manifest +import sbt.util.OptJsonWriter // T must be invariant to work properly. // Because it is sealed and the only instances go through AttributeKey.apply, diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/OptJsonWriter.scala b/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala similarity index 94% rename from internal/util-collection/src/main/scala/sbt/internal/util/OptJsonWriter.scala rename to internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala index f44ef44e8..3a2a23f25 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/OptJsonWriter.scala +++ b/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala @@ -1,4 +1,4 @@ -package sbt.internal.util +package sbt.util import sjsonnew.JsonWriter From 0713e31ceaded0cfd6b58277e4fecfeb468dfe1f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 21 Mar 2017 17:12:10 +0000 Subject: [PATCH 43/50] Allow opting out of the fallback OptJsonWriter Simply import OptJsonWriter.OptOut._ And you'll get the implicit lift, but not the implicit fallback. You get an ambiguous compile error like this: [error] /d/sbt-util/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala:28: ambiguous implicit values: [error] both method conflictingFallback1 in trait OptOut0 of type [A]=> sbt.util.NoJsonWriter[A] [error] and method conflictingFallback2 in trait OptOut0 of type [A]=> sbt.util.NoJsonWriter[A] [error] match expected type sbt.util.OptJsonWriter[Foo] [error] val x = implicitly[OptJsonWriter[Foo]] [error] ^ --- .../src/main/scala/sbt/util/OptJsonWriter.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala b/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala index 3a2a23f25..b7a793b46 100644 --- a/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala +++ b/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala @@ -11,4 +11,12 @@ trait OptJsonWriter0 { } object OptJsonWriter extends OptJsonWriter0 { implicit def lift[A](implicit z: JsonWriter[A]): SomeJsonWriter[A] = SomeJsonWriter(z) + + trait OptOut0 { + implicit def conflictingFallback1[A]: NoJsonWriter[A] = NoJsonWriter() + implicit def conflictingFallback2[A]: NoJsonWriter[A] = NoJsonWriter() + } + object OptOut extends OptOut0 { + implicit def lift[A](implicit z: JsonWriter[A]): SomeJsonWriter[A] = SomeJsonWriter(z) + } } From f0057fcdec302455ad0a2519bbe8ccdce4e2bf75 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 21 Mar 2017 21:50:45 +0000 Subject: [PATCH 44/50] Rename to StrictMode --- .../src/main/scala/sbt/util/OptJsonWriter.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala b/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala index b7a793b46..e840bc689 100644 --- a/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala +++ b/internal/util-collection/src/main/scala/sbt/util/OptJsonWriter.scala @@ -12,11 +12,11 @@ trait OptJsonWriter0 { object OptJsonWriter extends OptJsonWriter0 { implicit def lift[A](implicit z: JsonWriter[A]): SomeJsonWriter[A] = SomeJsonWriter(z) - trait OptOut0 { + trait StrictMode0 { implicit def conflictingFallback1[A]: NoJsonWriter[A] = NoJsonWriter() implicit def conflictingFallback2[A]: NoJsonWriter[A] = NoJsonWriter() } - object OptOut extends OptOut0 { + object StrictMode extends StrictMode0 { implicit def lift[A](implicit z: JsonWriter[A]): SomeJsonWriter[A] = SomeJsonWriter(z) } } From 090eab0736d1cbb773a00a8ecb521ac35558b5e7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 21 Jun 2017 11:36:02 +0100 Subject: [PATCH 45/50] Get rid of Eval --- .../src/main/scala/sbt/util/Eval.scala | 296 ------------------ 1 file changed, 296 deletions(-) delete mode 100644 internal/util-collection/src/main/scala/sbt/util/Eval.scala diff --git a/internal/util-collection/src/main/scala/sbt/util/Eval.scala b/internal/util-collection/src/main/scala/sbt/util/Eval.scala deleted file mode 100644 index abfb38070..000000000 --- a/internal/util-collection/src/main/scala/sbt/util/Eval.scala +++ /dev/null @@ -1,296 +0,0 @@ -package sbt.util - -import scala.annotation.tailrec - -// Copied from Cats (MIT license) - -/** - * Eval is a monad which controls evaluation. - * - * This type wraps a value (or a computation that produces a value) - * and can produce it on command via the `.value` method. - * - * There are three basic evaluation strategies: - * - * - Now: evaluated immediately - * - Later: evaluated once when value is needed - * - Always: evaluated every time value is needed - * - * The Later and Always are both lazy strategies while Now is eager. - * Later and Always are distinguished from each other only by - * memoization: once evaluated Later will save the value to be returned - * immediately if it is needed again. Always will run its computation - * every time. - * - * Eval supports stack-safe lazy computation via the .map and .flatMap - * methods, which use an internal trampoline to avoid stack overflows. - * Computation done within .map and .flatMap is always done lazily, - * even when applied to a Now instance. - * - * It is not generally good style to pattern-match on Eval instances. - * Rather, use .map and .flatMap to chain computation, and use .value - * to get the result when needed. It is also not good style to create - * Eval instances whose computation involves calling .value on another - * Eval instance -- this can defeat the trampolining and lead to stack - * overflows. - */ -sealed abstract class Eval[+A] extends Serializable { self => - - /** - * Evaluate the computation and return an A value. - * - * For lazy instances (Later, Always), any necessary computation - * will be performed at this point. For eager instances (Now), a - * value will be immediately returned. - */ - def value: A - - /** - * Transform an Eval[A] into an Eval[B] given the transformation - * function `f`. - * - * This call is stack-safe -- many .map calls may be chained without - * consumed additional stack during evaluation. - * - * Computation performed in f is always lazy, even when called on an - * eager (Now) instance. - */ - def map[B](f: A => B): Eval[B] = - flatMap(a => Now(f(a))) - - /** - * Lazily perform a computation based on an Eval[A], using the - * function `f` to produce an Eval[B] given an A. - * - * This call is stack-safe -- many .flatMap calls may be chained - * without consumed additional stack during evaluation. It is also - * written to avoid left-association problems, so that repeated - * calls to .flatMap will be efficiently applied. - * - * Computation performed in f is always lazy, even when called on an - * eager (Now) instance. - */ - def flatMap[B](f: A => Eval[B]): Eval[B] = - this match { - case c: Eval.Compute[A] => - new Eval.Compute[B] { - type Start = c.Start - // See https://issues.scala-lang.org/browse/SI-9931 for an explanation - // of why the type annotations are necessary in these two lines on - // Scala 2.12.0. - val start: () => Eval[Start] = c.start - val run: Start => Eval[B] = (s: c.Start) => - new Eval.Compute[B] { - type Start = A - val start = () => c.run(s) - val run = f - } - } - case c: Eval.Call[A] => - new Eval.Compute[B] { - type Start = A - val start = c.thunk - val run = f - } - case _ => - new Eval.Compute[B] { - type Start = A - val start = () => self - val run = f - } - } - - /** - * Ensure that the result of the computation (if any) will be - * memoized. - * - * Practically, this means that when called on an Always[A] a - * Later[A] with an equivalent computation will be returned. - */ - def memoize: Eval[A] -} - -/** - * Construct an eager Eval[A] instance. - * - * In some sense it is equivalent to using a val. - * - * This type should be used when an A value is already in hand, or - * when the computation to produce an A value is pure and very fast. - */ -final case class Now[A](value: A) extends Eval[A] { - def memoize: Eval[A] = this -} - -/** - * Construct a lazy Eval[A] instance. - * - * This type should be used for most "lazy" values. In some sense it - * is equivalent to using a lazy val. - * - * When caching is not required or desired (e.g. if the value produced - * may be large) prefer Always. When there is no computation - * necessary, prefer Now. - * - * Once Later has been evaluated, the closure (and any values captured - * by the closure) will not be retained, and will be available for - * garbage collection. - */ -final class Later[A](f: () => A) extends Eval[A] { - private[this] var thunk: () => A = f - - // The idea here is that `f` may have captured very large - // structures, but produce a very small result. In this case, once - // we've calculated a value, we would prefer to be able to free - // everything else. - // - // (For situations where `f` is small, but the output will be very - // expensive to store, consider using `Always`.) - lazy val value: A = { - val result = thunk() - thunk = null // scalastyle:off - result - } - - def memoize: Eval[A] = this -} - -object Later { - def apply[A](a: => A): Later[A] = new Later(a _) -} - -/** - * Construct a lazy Eval[A] instance. - * - * This type can be used for "lazy" values. In some sense it is - * equivalent to using a Function0 value. - * - * This type will evaluate the computation every time the value is - * required. It should be avoided except when laziness is required and - * caching must be avoided. Generally, prefer Later. - */ -final class Always[A](f: () => A) extends Eval[A] { - def value: A = f() - def memoize: Eval[A] = new Later(f) -} - -object Always { - def apply[A](a: => A): Always[A] = new Always(a _) -} - -object Eval { - - /** - * Construct an eager Eval[A] value (i.e. Now[A]). - */ - def now[A](a: A): Eval[A] = Now(a) - - /** - * Construct a lazy Eval[A] value with caching (i.e. Later[A]). - */ - def later[A](a: => A): Eval[A] = new Later(a _) - - /** - * Construct a lazy Eval[A] value without caching (i.e. Always[A]). - */ - def always[A](a: => A): Eval[A] = new Always(a _) - - /** - * Defer a computation which produces an Eval[A] value. - * - * This is useful when you want to delay execution of an expression - * which produces an Eval[A] value. Like .flatMap, it is stack-safe. - */ - def defer[A](a: => Eval[A]): Eval[A] = - new Eval.Call[A](a _) {} - - /** - * Static Eval instances for some common values. - * - * These can be useful in cases where the same values may be needed - * many times. - */ - val Unit: Eval[Unit] = Now(()) - val True: Eval[Boolean] = Now(true) - val False: Eval[Boolean] = Now(false) - val Zero: Eval[Int] = Now(0) - val One: Eval[Int] = Now(1) - - /** - * Call is a type of Eval[A] that is used to defer computations - * which produce Eval[A]. - * - * Users should not instantiate Call instances themselves. Instead, - * they will be automatically created when needed. - */ - sealed abstract class Call[A](val thunk: () => Eval[A]) extends Eval[A] { - def memoize: Eval[A] = new Later(() => value) - def value: A = Call.loop(this).value - } - - object Call { - /** Collapse the call stack for eager evaluations */ - @tailrec private def loop[A](fa: Eval[A]): Eval[A] = fa match { - case call: Eval.Call[A] => - loop(call.thunk()) - case compute: Eval.Compute[A] => - new Eval.Compute[A] { - type Start = compute.Start - val start: () => Eval[Start] = () => compute.start() - val run: Start => Eval[A] = s => loop1(compute.run(s)) - } - case other => other - } - - /** - * Alias for loop that can be called in a non-tail position - * from an otherwise tailrec-optimized loop. - */ - private def loop1[A](fa: Eval[A]): Eval[A] = loop(fa) - } - - /** - * Compute is a type of Eval[A] that is used to chain computations - * involving .map and .flatMap. Along with Eval#flatMap it - * implements the trampoline that guarantees stack-safety. - * - * Users should not instantiate Compute instances - * themselves. Instead, they will be automatically created when - * needed. - * - * Unlike a traditional trampoline, the internal workings of the - * trampoline are not exposed. This allows a slightly more efficient - * implementation of the .value method. - */ - sealed abstract class Compute[A] extends Eval[A] { - type Start - val start: () => Eval[Start] - val run: Start => Eval[A] - - def memoize: Eval[A] = Later(value) - - def value: A = { - type L = Eval[Any] - type C = Any => Eval[Any] - @tailrec def loop(curr: L, fs: List[C]): Any = - curr match { - case c: Compute[_] => - c.start() match { - case cc: Compute[_] => - loop( - cc.start().asInstanceOf[L], - cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs - ) - case xx => - loop(c.run(xx.value), fs) - } - case x => - fs match { - case f :: fs => loop(f(x.value), fs) - case Nil => x.value - } - } - loop(this.asInstanceOf[L], Nil).asInstanceOf[A] - } - } -} From cc3f46f9966b57a60bad948343950dcaba38e5ab Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 21 Jun 2017 14:41:23 +0100 Subject: [PATCH 46/50] Move HListFormats to collection to drop cache->collection dep Looks like the reason that util-cache depended on util-collection was to define the sjson-new formats (HListFormats) for util-collection's HList. Given that util-collection already depends on sjsonnew, HListFormats can also be defined in util-collection. All that was left then was (a) HListFormatSpec requires sjsonnewScalaJson, so that was added in test scope, and (b) HListFormats had to be dropped from sbt.util.CacheImplicits - HListFormats will have to be imported and/or mixed-in where required downstream. For importing convenience I defined a companion object. --- .../sbt/internal/util/HListFormats.scala | 70 +++++++++++++++++++ .../src/test/scala/HListFormatSpec.scala | 28 ++++++++ 2 files changed, 98 insertions(+) create mode 100644 internal/util-collection/src/main/scala/sbt/internal/util/HListFormats.scala create mode 100644 internal/util-collection/src/test/scala/HListFormatSpec.scala diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/HListFormats.scala b/internal/util-collection/src/main/scala/sbt/internal/util/HListFormats.scala new file mode 100644 index 000000000..6abae921c --- /dev/null +++ b/internal/util-collection/src/main/scala/sbt/internal/util/HListFormats.scala @@ -0,0 +1,70 @@ +package sbt +package internal +package util + +import sjsonnew._ +import Types.:+: + +trait HListFormats { + implicit val lnilFormat1: JsonFormat[HNil] = forHNil(HNil) + implicit val lnilFormat2: JsonFormat[HNil.type] = forHNil(HNil) + + private def forHNil[A <: HNil](hnil: A): JsonFormat[A] = new JsonFormat[A] { + def write[J](x: A, builder: Builder[J]): Unit = { + builder.beginArray() + builder.endArray() + } + + def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): A = jsOpt match { + case None => hnil + case Some(js) => unbuilder.beginArray(js); unbuilder.endArray(); hnil + } + } + + implicit def hconsFormat[H, T <: HList](implicit hf: JsonFormat[H], tf: HListJF[T]): JsonFormat[H :+: T] = + new JsonFormat[H :+: T] { + def write[J](hcons: H :+: T, builder: Builder[J]) = { + builder.beginArray() + hf.write(hcons.head, builder) + tf.write(hcons.tail, builder) + builder.endArray() + } + + def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]) = jsOpt match { + case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder)) + case Some(js) => + unbuilder.beginArray(js) + val hcons = HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder)) + unbuilder.endArray() + hcons + } + } + + trait HListJF[A <: HList] { + def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): A + def write[J](obj: A, builder: Builder[J]): Unit + } + + implicit def hconsHListJF[H, T <: HList](implicit hf: JsonFormat[H], tf: HListJF[T]): HListJF[H :+: T] = + new HListJF[H :+: T] { + def write[J](hcons: H :+: T, builder: Builder[J]) = { + hf.write(hcons.head, builder) + tf.write(hcons.tail, builder) + } + + def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]) = jsOpt match { + case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder)) + case Some(js) => HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder)) + } + } + + implicit val lnilHListJF1: HListJF[HNil] = hnilHListJF(HNil) + implicit val lnilHListJF2: HListJF[HNil.type] = hnilHListJF(HNil) + + implicit def hnilHListJF[A <: HNil](hnil: A): HListJF[A] = new HListJF[A] { + def write[J](hcons: A, builder: Builder[J]) = () + def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]) = hnil + } +} + +object HListFormats extends HListFormats diff --git a/internal/util-collection/src/test/scala/HListFormatSpec.scala b/internal/util-collection/src/test/scala/HListFormatSpec.scala new file mode 100644 index 000000000..8f6e9a73b --- /dev/null +++ b/internal/util-collection/src/test/scala/HListFormatSpec.scala @@ -0,0 +1,28 @@ +package sbt +package internal +package util + +import scalajson.ast.unsafe._ +import sjsonnew._, BasicJsonProtocol._, support.scalajson.unsafe._ +import HListFormats._ + +class HListFormatSpec extends UnitSpec { + val quux = 23 :+: "quux" :+: true :+: HNil + + it should "round trip quux" in assertRoundTrip(quux) + it should "round trip hnil" in assertRoundTrip(HNil) + + it should "have a flat structure for quux" in assertJsonString(quux, """[23,"quux",true]""") + it should "have a flat structure for hnil" in assertJsonString(HNil, "[]") + + def assertRoundTrip[A: JsonWriter: JsonReader](x: A) = { + val jsonString: String = toJsonString(x) + val jValue: JValue = Parser.parseUnsafe(jsonString) + val y: A = Converter.fromJson[A](jValue).get + assert(x === y) + } + + def assertJsonString[A: JsonWriter](x: A, s: String) = assert(toJsonString(x) === s) + + def toJsonString[A: JsonWriter](x: A): String = CompactPrinter(Converter.toJson(x).get) +} From 2cc93f2382030967188fc37a4169298b36128fd5 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 23 Jun 2017 14:06:53 +0100 Subject: [PATCH 47/50] Move Showlines to logging to drop logging->collection dep --- .../src/main/scala/sbt/util/ShowLines.scala | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 internal/util-collection/src/main/scala/sbt/util/ShowLines.scala diff --git a/internal/util-collection/src/main/scala/sbt/util/ShowLines.scala b/internal/util-collection/src/main/scala/sbt/util/ShowLines.scala deleted file mode 100644 index 65729d747..000000000 --- a/internal/util-collection/src/main/scala/sbt/util/ShowLines.scala +++ /dev/null @@ -1,15 +0,0 @@ -package sbt.util - -trait ShowLines[A] { - def showLines(a: A): Seq[String] -} -object ShowLines { - def apply[A](f: A => Seq[String]): ShowLines[A] = - new ShowLines[A] { - def showLines(a: A): Seq[String] = f(a) - } - - implicit class ShowLinesOp[A: ShowLines](a: A) { - def lines: Seq[String] = implicitly[ShowLines[A]].showLines(a) - } -} From a77d3146ab6427b72b6ceefef9d60fce9f139d7b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 23 Jun 2017 14:12:02 +0100 Subject: [PATCH 48/50] Breakout Position to drop lm->collection dep --- .../scala/sbt/internal/util/Positions.scala | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala deleted file mode 100644 index a11ae9c24..000000000 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Positions.scala +++ /dev/null @@ -1,20 +0,0 @@ -package sbt.internal.util - -sealed trait SourcePosition - -sealed trait FilePosition extends SourcePosition { - def path: String - def startLine: Int -} - -case object NoPosition extends SourcePosition - -final case class LinePosition(path: String, startLine: Int) extends FilePosition - -final case class LineRange(start: Int, end: Int) { - def shift(n: Int) = new LineRange(start + n, end + n) -} - -final case class RangePosition(path: String, range: LineRange) extends FilePosition { - def startLine = range.start -} From 9494967e055a64389f4b002e3485ac0f0367579d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 26 Jun 2017 11:38:37 +0100 Subject: [PATCH 49/50] Upgrade util/lm/zinc & config build for util modules Welcome back home! --- build.sbt | 75 +++++++++++++------ .../src/test/scala/UnitSpec.scala | 5 ++ .../src/test/scala/UnitSpec.scala | 5 ++ main-actions/src/main/scala/sbt/Package.scala | 1 + .../src/main/scala/sbt/RawCompileLike.scala | 1 + .../internal/AltLibraryManagementCodec.scala | 1 + .../sbt/internal/LibraryManagement.scala | 1 + project/Dependencies.scala | 65 +++++++--------- project/Util.scala | 18 +++-- .../cache-update/build.sbt | 1 + 10 files changed, 103 insertions(+), 70 deletions(-) create mode 100644 internal/util-collection/src/test/scala/UnitSpec.scala create mode 100644 internal/util-complete/src/test/scala/UnitSpec.scala diff --git a/build.sbt b/build.sbt index 8bd3aa316..df186e3cd 100644 --- a/build.sbt +++ b/build.sbt @@ -124,6 +124,34 @@ lazy val bundledLauncherProj = /* ** subproject declarations ** */ +val collectionProj = (project in file("internal") / "util-collection") + .settings( + testedBaseSettings, + Util.keywordsSettings, + name := "Collections", + libraryDependencies ++= Seq(sjsonNewScalaJson.value) + ) + .configure(addSbtUtilPosition) + +// Command line-related utilities. +val completeProj = (project in file("internal") / "util-complete") + .dependsOn(collectionProj) + .settings( + testedBaseSettings, + name := "Completion", + libraryDependencies += jline + ) + .configure(addSbtIO, addSbtUtilControl) + +// A logic with restricted negation as failure for a unique, stable model +val logicProj = (project in file("internal") / "util-logic") + .dependsOn(collectionProj) + .settings( + testedBaseSettings, + name := "Logic" + ) + .configure(addSbtUtilRelation) + /* **** Intermediate-level Modules **** */ // Runner for uniform test interface @@ -154,21 +182,23 @@ lazy val testAgentProj = (project in file("testing") / "agent") // Basic task engine lazy val taskProj = (project in file("tasks")) + .dependsOn(collectionProj) .settings( testedBaseSettings, name := "Tasks" ) - .configure(addSbtUtilControl, addSbtUtilCollection) + .configure(addSbtUtilControl) // Standard task system. This provides map, flatMap, join, and more on top of the basic task model. lazy val stdTaskProj = (project in file("tasks-standard")) + .dependsOn(collectionProj) .dependsOn(taskProj % "compile;test->test") .settings( testedBaseSettings, name := "Task System", testExclusive ) - .configure(addSbtUtilCollection, addSbtUtilLogging, addSbtUtilCache, addSbtIO) + .configure(addSbtIO, addSbtUtilLogging, addSbtUtilCache) // Embedded Scala code runner lazy val runProj = (project in file("run")) @@ -201,24 +231,23 @@ lazy val scriptedPluginProj = (project in scriptedPath / "plugin") // Implementation and support code for defining actions. lazy val actionsProj = (project in file("main-actions")) - .dependsOn(runProj, stdTaskProj, taskProj, testingProj) + .dependsOn(completeProj, runProj, stdTaskProj, taskProj, testingProj) .settings( testedBaseSettings, name := "Actions", libraryDependencies += sjsonNewScalaJson.value ) .configure( - addSbtCompilerClasspath, - addSbtUtilCompletion, - addSbtCompilerApiInfo, - addSbtZinc, - addSbtCompilerIvyIntegration, - addSbtCompilerInterface, addSbtIO, addSbtUtilLogging, addSbtUtilRelation, + addSbtCompilerInterface, + addSbtCompilerClasspath, + addSbtCompilerApiInfo, + addSbtUtilTracking, addSbtLm, - addSbtUtilTracking + addSbtCompilerIvyIntegration, + addSbtZinc ) lazy val protocolProj = (project in file("protocol")) @@ -237,7 +266,7 @@ lazy val protocolProj = (project in file("protocol")) // General command support and core commands not specific to a build system lazy val commandProj = (project in file("main-command")) .enablePlugins(ContrabandPlugin, JsonCodecPlugin) - .dependsOn(protocolProj) + .dependsOn(protocolProj, completeProj) .settings( testedBaseSettings, name := "Command", @@ -248,10 +277,9 @@ lazy val commandProj = (project in file("main-command")) contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats ) .configure( - addSbtCompilerInterface, addSbtIO, addSbtUtilLogging, - addSbtUtilCompletion, + addSbtCompilerInterface, addSbtCompilerClasspath, addSbtLm ) @@ -259,12 +287,12 @@ lazy val commandProj = (project in file("main-command")) // The core macro project defines the main logic of the DSL, abstracted // away from several sbt implementators (tasks, settings, et cetera). lazy val coreMacrosProj = (project in file("core-macros")) + .dependsOn(collectionProj) .settings( commonSettings, name := "Core Macros", libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value ) - .configure(addSbtUtilCollection) /* Write all the compile-time dependencies of the spores macro to a file, * in order to read it from the created Toolbox to run the neg tests. */ @@ -288,19 +316,18 @@ lazy val generateToolboxClasspath = Def.task { // Fixes scope=Scope for Setting (core defined in collectionProj) to define the settings system used in build definitions lazy val mainSettingsProj = (project in file("main-settings")) - .dependsOn(commandProj, stdTaskProj, coreMacrosProj) + .dependsOn(completeProj, commandProj, stdTaskProj, coreMacrosProj) .settings( testedBaseSettings, name := "Main Settings", resourceGenerators in Compile += generateToolboxClasspath.taskValue ) .configure( - addSbtUtilCache, - addSbtCompilerInterface, - addSbtUtilRelation, - addSbtUtilLogging, addSbtIO, - addSbtUtilCompletion, + addSbtUtilLogging, + addSbtUtilCache, + addSbtUtilRelation, + addSbtCompilerInterface, addSbtCompilerClasspath, addSbtLm ) @@ -308,7 +335,7 @@ lazy val mainSettingsProj = (project in file("main-settings")) // The main integration project for sbt. It brings all of the projects together, configures them, and provides for overriding conventions. lazy val mainProj = (project in file("main")) .enablePlugins(ContrabandPlugin) - .dependsOn(actionsProj, mainSettingsProj, runProj, commandProj) + .dependsOn(logicProj, actionsProj, mainSettingsProj, runProj, commandProj) .settings( testedBaseSettings, name := "Main", @@ -318,10 +345,9 @@ lazy val mainProj = (project in file("main")) sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala" ) .configure( - addSbtCompilerInterface, addSbtIO, addSbtUtilLogging, - addSbtUtilLogic, + addSbtCompilerInterface, addSbtLm, addSbtZincCompile ) @@ -379,6 +405,9 @@ lazy val publishLauncher = TaskKey[Unit]("publish-launcher") def allProjects = Seq( + collectionProj, + logicProj, + completeProj, testingProj, testAgentProj, taskProj, diff --git a/internal/util-collection/src/test/scala/UnitSpec.scala b/internal/util-collection/src/test/scala/UnitSpec.scala new file mode 100644 index 000000000..99ad43c2d --- /dev/null +++ b/internal/util-collection/src/test/scala/UnitSpec.scala @@ -0,0 +1,5 @@ +package sbt.internal.util + +import org.scalatest._ + +abstract class UnitSpec extends FlatSpec with Matchers diff --git a/internal/util-complete/src/test/scala/UnitSpec.scala b/internal/util-complete/src/test/scala/UnitSpec.scala new file mode 100644 index 000000000..99ad43c2d --- /dev/null +++ b/internal/util-complete/src/test/scala/UnitSpec.scala @@ -0,0 +1,5 @@ +package sbt.internal.util + +import org.scalatest._ + +abstract class UnitSpec extends FlatSpec with Matchers diff --git a/main-actions/src/main/scala/sbt/Package.scala b/main-actions/src/main/scala/sbt/Package.scala index 628730c9d..fbf211416 100644 --- a/main-actions/src/main/scala/sbt/Package.scala +++ b/main-actions/src/main/scala/sbt/Package.scala @@ -16,6 +16,7 @@ import sbt.util.Logger import sbt.util.{ CacheStoreFactory, FilesInfo, ModifiedFileInfo, PlainFileInfo } import sbt.internal.util.HNil +import sbt.internal.util.HListFormats._ import sbt.util.FileInfo.{ exists, lastModified } import sbt.util.CacheImplicits._ import sbt.util.Tracked.inputChanged diff --git a/main-actions/src/main/scala/sbt/RawCompileLike.scala b/main-actions/src/main/scala/sbt/RawCompileLike.scala index bd2a20618..543590277 100644 --- a/main-actions/src/main/scala/sbt/RawCompileLike.scala +++ b/main-actions/src/main/scala/sbt/RawCompileLike.scala @@ -11,6 +11,7 @@ import sbt.io.syntax._ import sbt.io.IO import sbt.internal.util.Types.:+: +import sbt.internal.util.HListFormats._ import sbt.util.CacheImplicits._ import sbt.util.Tracked.inputChanged import sbt.util.{ CacheStoreFactory, FilesInfo, HashFileInfo, ModifiedFileInfo, PlainFileInfo } diff --git a/main/src/main/scala/sbt/internal/AltLibraryManagementCodec.scala b/main/src/main/scala/sbt/internal/AltLibraryManagementCodec.scala index 8ceed18fb..5fb7ca469 100644 --- a/main/src/main/scala/sbt/internal/AltLibraryManagementCodec.scala +++ b/main/src/main/scala/sbt/internal/AltLibraryManagementCodec.scala @@ -3,6 +3,7 @@ package sbt.internal import sbt.internal.librarymanagement._ import sbt.internal.util.Types._ import sbt.internal.util.{ HList, HNil } +import sbt.internal.util.HListFormats._ import sbt.io.{ Hash, IO } import sbt.librarymanagement._ import sbt.util.CacheImplicits._ diff --git a/main/src/main/scala/sbt/internal/LibraryManagement.scala b/main/src/main/scala/sbt/internal/LibraryManagement.scala index ac2894d3a..17474b990 100644 --- a/main/src/main/scala/sbt/internal/LibraryManagement.scala +++ b/main/src/main/scala/sbt/internal/LibraryManagement.scala @@ -5,6 +5,7 @@ import java.io.File import sbt.internal.librarymanagement._ import sbt.internal.util.HNil import sbt.internal.util.Types._ +import sbt.internal.util.HListFormats._ import sbt.librarymanagement._ import sbt.librarymanagement.syntax._ import sbt.util.CacheImplicits._ diff --git a/project/Dependencies.scala b/project/Dependencies.scala index bac99155a..b65ce1108 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -13,22 +13,19 @@ object Dependencies { // sbt modules private val ioVersion = "1.0.0-M12" - private val utilVersion = "1.0.0-M25" - private val lmVersion = "1.0.0-X16" - private val zincVersion = "1.0.0-X17" + private val utilVersion = "1.0.0-M26" + private val lmVersion = "1.0.0-X17" + private val zincVersion = "1.0.0-X18" private val sbtIO = "org.scala-sbt" %% "io" % ioVersion - private val utilCache = "org.scala-sbt" %% "util-cache" % utilVersion - private val utilCollection = "org.scala-sbt" %% "util-collection" % utilVersion - private val utilCompletion = "org.scala-sbt" %% "util-completion" % utilVersion - private val utilControl = "org.scala-sbt" %% "util-control" % utilVersion + private val utilPosition = "org.scala-sbt" %% "util-position" % utilVersion private val utilLogging = "org.scala-sbt" %% "util-logging" % utilVersion - private val utilLogic = "org.scala-sbt" %% "util-logic" % utilVersion + private val utilCache = "org.scala-sbt" %% "util-cache" % utilVersion + private val utilControl = "org.scala-sbt" %% "util-control" % utilVersion private val utilRelation = "org.scala-sbt" %% "util-relation" % utilVersion - private val utilScripted = "org.scala-sbt" %% "util-scripted" % utilVersion - private val utilTesting = "org.scala-sbt" %% "util-testing" % utilVersion private val utilTracking = "org.scala-sbt" %% "util-tracking" % utilVersion + private val utilScripted = "org.scala-sbt" %% "util-scripted" % utilVersion private val libraryManagement = "org.scala-sbt" %% "librarymanagement" % lmVersion @@ -36,10 +33,10 @@ object Dependencies { val rawLauncher = "org.scala-sbt" % "launcher" % "1.0.0" val testInterface = "org.scala-sbt" % "test-interface" % "1.0" + private val compilerInterface = "org.scala-sbt" % "compiler-interface" % zincVersion + private val compilerClasspath = "org.scala-sbt" %% "zinc-classpath" % zincVersion private val compilerApiInfo = "org.scala-sbt" %% "zinc-apiinfo" % zincVersion private val compilerBridge = "org.scala-sbt" %% "compiler-bridge" % zincVersion - private val compilerClasspath = "org.scala-sbt" %% "zinc-classpath" % zincVersion - private val compilerInterface = "org.scala-sbt" % "compiler-interface" % zincVersion private val compilerIvyIntegration = "org.scala-sbt" %% "zinc-ivy-integration" % zincVersion private val zinc = "org.scala-sbt" %% "zinc" % zincVersion private val zincCompile = "org.scala-sbt" %% "zinc-compile" % zincVersion @@ -57,51 +54,39 @@ object Dependencies { lazy val sbtLmPath = getSbtModulePath("sbtlm.path", "sbt/lm") lazy val sbtZincPath = getSbtModulePath("sbtzinc.path", "sbt/zinc") - def addSbtModule( - p: Project, - path: Option[String], - projectName: String, - m: ModuleID, - c: Option[Configuration] = None - ) = + def addSbtModule(p: Project, path: Option[String], projectName: String, m: ModuleID) = path match { - case Some(f) => - p dependsOn c.fold[ClasspathDep[ProjectReference]](ProjectRef(file(f), projectName))( - ProjectRef(file(f), projectName) % _) - case None => p settings (libraryDependencies += c.fold(m)(m % _)) + case Some(f) => p dependsOn ProjectRef(file(f), projectName) + case None => p settings (libraryDependencies += m) } def addSbtIO(p: Project): Project = addSbtModule(p, sbtIoPath, "io", sbtIO) - def addSbtUtilCache(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilCache", utilCache) - def addSbtUtilCollection(p: Project): Project = - addSbtModule(p, sbtUtilPath, "utilCollection", utilCollection) - def addSbtUtilCompletion(p: Project): Project = - addSbtModule(p, sbtUtilPath, "utilComplete", utilCompletion) - def addSbtUtilControl(p: Project): Project = - addSbtModule(p, sbtUtilPath, "utilControl", utilControl) + def addSbtUtilPosition(p: Project): Project = + addSbtModule(p, sbtUtilPath, "utilPosition", utilPosition) def addSbtUtilLogging(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilLogging", utilLogging) - def addSbtUtilLogic(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilLogic", utilLogic) + def addSbtUtilCache(p: Project): Project = + addSbtModule(p, sbtUtilPath, "utilCache", utilCache) + def addSbtUtilControl(p: Project): Project = + addSbtModule(p, sbtUtilPath, "utilControl", utilControl) def addSbtUtilRelation(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilRelation", utilRelation) - def addSbtUtilScripted(p: Project): Project = - addSbtModule(p, sbtUtilPath, "utilScripted", utilScripted) - def addSbtUtilTesting(p: Project): Project = - addSbtModule(p, sbtUtilPath, "utilTesting", utilTesting, Some(Test)) def addSbtUtilTracking(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilTracking", utilTracking) + def addSbtUtilScripted(p: Project): Project = + addSbtModule(p, sbtUtilPath, "utilScripted", utilScripted) def addSbtLm(p: Project): Project = addSbtModule(p, sbtLmPath, "lm", libraryManagement) + def addSbtCompilerInterface(p: Project): Project = + addSbtModule(p, sbtZincPath, "compilerInterface", compilerInterface) + def addSbtCompilerClasspath(p: Project): Project = + addSbtModule(p, sbtZincPath, "zincClasspath", compilerClasspath) def addSbtCompilerApiInfo(p: Project): Project = addSbtModule(p, sbtZincPath, "zincApiInfo", compilerApiInfo) def addSbtCompilerBridge(p: Project): Project = addSbtModule(p, sbtZincPath, "compilerBridge", compilerBridge) - def addSbtCompilerClasspath(p: Project): Project = - addSbtModule(p, sbtZincPath, "zincClasspath", compilerClasspath) - def addSbtCompilerInterface(p: Project): Project = - addSbtModule(p, sbtZincPath, "compilerInterface", compilerInterface) def addSbtCompilerIvyIntegration(p: Project): Project = addSbtModule(p, sbtZincPath, "zincIvyIntegration", compilerIvyIntegration) def addSbtZinc(p: Project): Project = addSbtModule(p, sbtZincPath, "zinc", zinc) @@ -109,6 +94,8 @@ object Dependencies { addSbtModule(p, sbtZincPath, "zincCompile", zincCompile) val sjsonNewScalaJson = Def.setting { "com.eed3si9n" %% "sjson-new-scalajson" % contrabandSjsonNewVersion.value } + + val jline = "jline" % "jline" % "2.14.4" val scalatest = "org.scalatest" %% "scalatest" % "3.0.1" val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.13.4" val specs2 = "org.specs2" %% "specs2" % "2.4.17" diff --git a/project/Util.scala b/project/Util.scala index bfb02bd2b..8a4b408bc 100644 --- a/project/Util.scala +++ b/project/Util.scala @@ -162,13 +162,14 @@ object Util { def writeScalaKeywords(base: File, keywords: Set[String]): File = { val init = keywords.map(tn => '"' + tn + '"').mkString("Set(", ", ", ")") val ObjectName = "ScalaKeywords" - val PackageName = "sbt" - val keywordsSrc = - """package %s -object %s { - val values = %s -}""".format(PackageName, ObjectName, init) - val out = base / PackageName.replace('.', '/') / (ObjectName + ".scala") + val PackageName = "sbt.internal.util" + val keywordsSrc = s""" + |package $PackageName + |object $ObjectName { + | val values = $init + |} + """.trim.stripMargin + val out = base / PackageName.replace('.', '/') / s"$ObjectName.scala" IO.write(out, keywordsSrc) out } @@ -179,7 +180,8 @@ object %s { scalaKeywords := getScalaKeywords, generateKeywords := writeScalaKeywords(sourceManaged.value, scalaKeywords.value), sourceGenerators += Def.task(Seq(generateKeywords.value)).taskValue - )) + ) + ) } object Licensed { diff --git a/sbt/src/sbt-test/dependency-management/cache-update/build.sbt b/sbt/src/sbt-test/dependency-management/cache-update/build.sbt index 38eb67043..5afcc2440 100644 --- a/sbt/src/sbt-test/dependency-management/cache-update/build.sbt +++ b/sbt/src/sbt-test/dependency-management/cache-update/build.sbt @@ -24,6 +24,7 @@ lazy val root = (project in file(".")) type In = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil import sbt.util.CacheImplicits._ + import sbt.internal.util.HListFormats._ import sbt.internal.AltLibraryManagementCodec._ val f: In => Unit = From dc2d4d613f2f8a14730ec09e8720ca4ae659a746 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 7 Jul 2017 10:43:52 +0100 Subject: [PATCH 50/50] Format in-sourced util modules --- .../main/scala/sbt/internal/util/AList.scala | 132 ++- .../scala/sbt/internal/util/Attributes.scala | 112 ++- .../scala/sbt/internal/util/Classes.scala | 11 +- .../main/scala/sbt/internal/util/Dag.scala | 139 ++-- .../main/scala/sbt/internal/util/HList.scala | 8 +- .../sbt/internal/util/HListFormats.scala | 18 +- .../main/scala/sbt/internal/util/IDSet.scala | 18 +- .../main/scala/sbt/internal/util/INode.scala | 114 ++- .../main/scala/sbt/internal/util/KList.scala | 30 +- .../main/scala/sbt/internal/util/PMap.scala | 48 +- .../scala/sbt/internal/util/Settings.scala | 767 ++++++++++++------ .../main/scala/sbt/internal/util/Signal.scala | 59 +- .../sbt/internal/util/TypeFunctions.scala | 10 +- .../main/scala/sbt/internal/util/Util.scala | 9 +- .../src/test/scala/DagSpecification.scala | 66 +- .../src/test/scala/KeyTest.scala | 18 +- .../src/test/scala/LiteralTest.scala | 4 +- .../src/test/scala/PMapTest.scala | 2 +- .../src/test/scala/SettingsExample.scala | 54 +- .../src/test/scala/SettingsTest.scala | 250 +++--- .../scala/sbt/internal/util/LineReader.scala | 106 +-- .../internal/util/complete/Completions.scala | 35 +- .../internal/util/complete/EditDistance.scala | 13 +- .../util/complete/ExampleSource.scala | 16 +- .../sbt/internal/util/complete/History.scala | 24 +- .../util/complete/HistoryCommands.scala | 33 +- .../util/complete/JLineCompletion.scala | 169 ++-- .../sbt/internal/util/complete/Parser.scala | 412 ++++++---- .../sbt/internal/util/complete/Parsers.scala | 72 +- .../internal/util/complete/ProcessError.scala | 51 +- .../util/complete/TokenCompletions.scala | 18 +- .../internal/util/complete/TypeString.scala | 21 +- .../internal/util/complete/UpperBound.scala | 12 + .../src/test/scala/ParserTest.scala | 42 +- .../scala/sbt/complete/FileExamplesTest.scala | 55 +- .../sbt/complete/FixedSetExamplesTest.scala | 3 +- .../sbt/complete/ParserWithExamplesTest.scala | 56 +- .../scala/sbt/internal/util/logic/Logic.scala | 207 +++-- 38 files changed, 1975 insertions(+), 1239 deletions(-) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala index 3247e9a8a..bbb20800d 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/AList.scala @@ -14,11 +14,14 @@ trait AList[K[L[x]]] { def foldr[M[_], A](value: K[M], f: (M[_], A) => A, init: A): A def toList[M[_]](value: K[M]): List[M[_]] = foldr[M, List[M[_]]](value, _ :: _, Nil) + def apply[M[_], C](value: K[M], f: K[Id] => C)(implicit a: Applicative[M]): M[C] = a.map(f, traverse[M, M, Id](value, idK[M])(a)) } + object AList { type Empty = AList[({ type l[L[x]] = Unit })#l] + /** AList for Unit, which represents a sequence that is always empty.*/ val empty: Empty = new Empty { def transform[M[_], N[_]](in: Unit, f: M ~> N) = () @@ -28,21 +31,23 @@ object AList { } type SeqList[T] = AList[({ type l[L[x]] = List[L[T]] })#l] + /** AList for a homogeneous sequence. */ def seq[T]: SeqList[T] = new SeqList[T] { def transform[M[_], N[_]](s: List[M[T]], f: M ~> N) = s.map(f.fn[T]) def foldr[M[_], A](s: List[M[T]], f: (M[_], A) => A, init: A): A = (init /: s.reverse)((t, m) => f(m, t)) - override def apply[M[_], C](s: List[M[T]], f: List[T] => C)(implicit ap: Applicative[M]): M[C] = - { - def loop[V](in: List[M[T]], g: List[T] => V): M[V] = - in match { - case Nil => ap.pure(g(Nil)) - case x :: xs => - val h = (ts: List[T]) => (t: T) => g(t :: ts) - ap.apply(loop(xs, h), x) - } - loop(s, f) - } + + override def apply[M[_], C](s: List[M[T]], f: List[T] => C)(implicit ap: Applicative[M]): M[C] = { + def loop[V](in: List[M[T]], g: List[T] => V): M[V] = + in match { + case Nil => ap.pure(g(Nil)) + case x :: xs => + val h = (ts: List[T]) => (t: T) => g(t :: ts) + ap.apply(loop(xs, h), x) + } + loop(s, f) + } + def traverse[M[_], N[_], P[_]](s: List[M[T]], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[List[P[T]]] = ??? } @@ -55,8 +60,9 @@ object AList { override def toList[M[_]](k: KL[M]) = k.toList } - /** AList for a single value. */ type Single[A] = AList[({ type l[L[x]] = L[A] })#l] + + /** AList for a single value. */ def single[A]: Single[A] = new Single[A] { def transform[M[_], N[_]](a: M[A], f: M ~> N) = f(a) def foldr[M[_], T](a: M[A], f: (M[_], T) => T, init: T): T = f(a, init) @@ -64,17 +70,18 @@ object AList { } type ASplit[K[L[x]], B[x]] = AList[({ type l[L[x]] = K[(L ∙ B)#l] })#l] + /** AList that operates on the outer type constructor `A` of a composition `[x] A[B[x]]` for type constructors `A` and `B`*/ def asplit[K[L[x]], B[x]](base: AList[K]): ASplit[K, B] = new ASplit[K, B] { type Split[L[x]] = K[(L ∙ B)#l] + def transform[M[_], N[_]](value: Split[M], f: M ~> N): Split[N] = base.transform[(M ∙ B)#l, (N ∙ B)#l](value, nestCon[M, N, B](f)) - def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Split[P]] = - { - val g = nestCon[M, (N ∙ P)#l, B](f) - base.traverse[(M ∙ B)#l, N, (P ∙ B)#l](value, g)(np) - } + def traverse[M[_], N[_], P[_]](value: Split[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Split[P]] = { + val g = nestCon[M, (N ∙ P)#l, B](f) + base.traverse[(M ∙ B)#l, N, (P ∙ B)#l](value, g)(np) + } def foldr[M[_], A](value: Split[M], f: (M[_], A) => A, init: A): A = base.foldr[(M ∙ B)#l, A](value, f, init) @@ -87,11 +94,10 @@ object AList { type T2[M[_]] = (M[A], M[B]) def transform[M[_], N[_]](t: T2[M], f: M ~> N): T2[N] = (f(t._1), f(t._2)) def foldr[M[_], T](t: T2[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, init)) - def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T2[P]] = - { - val g = (Tuple2.apply[P[A], P[B]] _).curried - np.apply(np.map(g, f(t._1)), f(t._2)) - } + def traverse[M[_], N[_], P[_]](t: T2[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T2[P]] = { + val g = (Tuple2.apply[P[A], P[B]] _).curried + np.apply(np.map(g, f(t._1)), f(t._2)) + } } sealed trait T3K[A, B, C] { type l[L[x]] = (L[A], L[B], L[C]) } @@ -100,11 +106,10 @@ object AList { type T3[M[_]] = (M[A], M[B], M[C]) def transform[M[_], N[_]](t: T3[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3)) def foldr[M[_], T](t: T3[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, init))) - def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T3[P]] = - { - val g = (Tuple3.apply[P[A], P[B], P[C]] _).curried - np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)) - } + def traverse[M[_], N[_], P[_]](t: T3[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T3[P]] = { + val g = (Tuple3.apply[P[A], P[B], P[C]] _).curried + np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)) + } } sealed trait T4K[A, B, C, D] { type l[L[x]] = (L[A], L[B], L[C], L[D]) } @@ -113,11 +118,10 @@ object AList { type T4[M[_]] = (M[A], M[B], M[C], M[D]) def transform[M[_], N[_]](t: T4[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4)) def foldr[M[_], T](t: T4[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, init)))) - def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T4[P]] = - { - val g = (Tuple4.apply[P[A], P[B], P[C], P[D]] _).curried - np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)) - } + def traverse[M[_], N[_], P[_]](t: T4[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T4[P]] = { + val g = (Tuple4.apply[P[A], P[B], P[C], P[D]] _).curried + np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)) + } } sealed trait T5K[A, B, C, D, E] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E]) } @@ -126,11 +130,10 @@ object AList { type T5[M[_]] = (M[A], M[B], M[C], M[D], M[E]) def transform[M[_], N[_]](t: T5[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5)) def foldr[M[_], T](t: T5[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, init))))) - def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T5[P]] = - { - val g = (Tuple5.apply[P[A], P[B], P[C], P[D], P[E]] _).curried - np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)) - } + def traverse[M[_], N[_], P[_]](t: T5[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T5[P]] = { + val g = (Tuple5.apply[P[A], P[B], P[C], P[D], P[E]] _).curried + np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)) + } } sealed trait T6K[A, B, C, D, E, F] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F]) } @@ -139,11 +142,10 @@ object AList { type T6[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F]) def transform[M[_], N[_]](t: T6[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6)) def foldr[M[_], T](t: T6[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, init)))))) - def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T6[P]] = - { - val g = (Tuple6.apply[P[A], P[B], P[C], P[D], P[E], P[F]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)) - } + def traverse[M[_], N[_], P[_]](t: T6[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T6[P]] = { + val g = (Tuple6.apply[P[A], P[B], P[C], P[D], P[E], P[F]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)) + } } sealed trait T7K[A, B, C, D, E, F, G] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G]) } @@ -152,23 +154,22 @@ object AList { type T7[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G]) def transform[M[_], N[_]](t: T7[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7)) def foldr[M[_], T](t: T7[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, init))))))) - def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T7[P]] = - { - val g = (Tuple7.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)) - } + def traverse[M[_], N[_], P[_]](t: T7[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T7[P]] = { + val g = (Tuple7.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)) + } } + sealed trait T8K[A, B, C, D, E, F, G, H] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H]) } type T8List[A, B, C, D, E, F, G, H] = AList[T8K[A, B, C, D, E, F, G, H]#l] def tuple8[A, B, C, D, E, F, G, H]: T8List[A, B, C, D, E, F, G, H] = new T8List[A, B, C, D, E, F, G, H] { type T8[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H]) def transform[M[_], N[_]](t: T8[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8)) def foldr[M[_], T](t: T8[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, init)))))))) - def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T8[P]] = - { - val g = (Tuple8.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)) - } + def traverse[M[_], N[_], P[_]](t: T8[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T8[P]] = { + val g = (Tuple8.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)) + } } sealed trait T9K[A, B, C, D, E, F, G, H, I] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I]) } @@ -177,11 +178,10 @@ object AList { type T9[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I]) def transform[M[_], N[_]](t: T9[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9)) def foldr[M[_], T](t: T9[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, init))))))))) - def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T9[P]] = - { - val g = (Tuple9.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)) - } + def traverse[M[_], N[_], P[_]](t: T9[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T9[P]] = { + val g = (Tuple9.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)) + } } sealed trait T10K[A, B, C, D, E, F, G, H, I, J] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J]) } @@ -190,11 +190,10 @@ object AList { type T10[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J]) def transform[M[_], N[_]](t: T10[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10)) def foldr[M[_], T](t: T10[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, init)))))))))) - def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T10[P]] = - { - val g = (Tuple10.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)) - } + def traverse[M[_], N[_], P[_]](t: T10[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T10[P]] = { + val g = (Tuple10.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)) + } } sealed trait T11K[A, B, C, D, E, F, G, H, I, J, K] { type l[L[x]] = (L[A], L[B], L[C], L[D], L[E], L[F], L[G], L[H], L[I], L[J], L[K]) } @@ -203,10 +202,9 @@ object AList { type T11[M[_]] = (M[A], M[B], M[C], M[D], M[E], M[F], M[G], M[H], M[I], M[J], M[K]) def transform[M[_], N[_]](t: T11[M], f: M ~> N) = (f(t._1), f(t._2), f(t._3), f(t._4), f(t._5), f(t._6), f(t._7), f(t._8), f(t._9), f(t._10), f(t._11)) def foldr[M[_], T](t: T11[M], f: (M[_], T) => T, init: T): T = f(t._1, f(t._2, f(t._3, f(t._4, f(t._5, f(t._6, f(t._7, f(t._8, f(t._9, f(t._10, f(t._11, init))))))))))) - def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T11[P]] = - { - val g = (Tuple11.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J], P[K]] _).curried - np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)), f(t._11)) - } + def traverse[M[_], N[_], P[_]](t: T11[M], f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[T11[P]] = { + val g = (Tuple11.apply[P[A], P[B], P[C], P[D], P[E], P[F], P[G], P[H], P[I], P[J], P[K]] _).curried + np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.apply(np.map(g, f(t._1)), f(t._2)), f(t._3)), f(t._4)), f(t._5)), f(t._6)), f(t._7)), f(t._8)), f(t._9)), f(t._10)), f(t._11)) + } } } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index 5cf6fb65b..ce0cb8e29 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -42,54 +42,75 @@ sealed trait AttributeKey[T] { def rank: Int def optJsonWriter: OptJsonWriter[T] + } + private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] { override final def toString = label override final def hashCode = label.hashCode - override final def equals(o: Any) = (this eq o.asInstanceOf[AnyRef]) || (o match { - case a: SharedAttributeKey[t] => a.label == this.label && a.manifest == this.manifest - case _ => false - }) + override final def equals(o: Any) = + (this eq o.asInstanceOf[AnyRef]) || (o match { + case a: SharedAttributeKey[t] => a.label == this.label && a.manifest == this.manifest + case _ => false + }) final def isLocal: Boolean = false } + object AttributeKey { - def apply[T](name: String)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter](name: String): AttributeKey[T] = make(name, None, Nil, Int.MaxValue) - def apply[T](name: String, rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter](name: String, rank: Int): AttributeKey[T] = make(name, None, Nil, rank) - def apply[T](name: String, description: String)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter](name: String, description: String): AttributeKey[T] = apply(name, description, Nil) - def apply[T](name: String, description: String, rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter](name: String, + description: String, + rank: Int): AttributeKey[T] = apply(name, description, Nil, rank) - def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]])(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter](name: String, + description: String, + extend: Seq[AttributeKey[_]]): AttributeKey[T] = apply(name, description, extend, Int.MaxValue) - def apply[T](name: String, description: String, extend: Seq[AttributeKey[_]], rank: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = + def apply[T: Manifest: OptJsonWriter](name: String, + description: String, + extend: Seq[AttributeKey[_]], + rank: Int): AttributeKey[T] = make(name, Some(description), extend, rank) - private[this] def make[T](name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int)(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = new SharedAttributeKey[T] { - def manifest = mf - val label = Util.hyphenToCamel(name) - def description = description0 - def extend = extend0 - def rank = rank0 - def optJsonWriter = ojw - } - private[sbt] def local[T](implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = new AttributeKey[T] { - def manifest = mf - def label = LocalLabel - def description = None - def extend = Nil - override def toString = label - def isLocal: Boolean = true - def rank = Int.MaxValue - val optJsonWriter = ojw - } + private[this] def make[T]( + name: String, + description0: Option[String], + extend0: Seq[AttributeKey[_]], + rank0: Int + )(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = + new SharedAttributeKey[T] { + def manifest = mf + val label = Util.hyphenToCamel(name) + def description = description0 + def extend = extend0 + def rank = rank0 + def optJsonWriter = ojw + } + + private[sbt] def local[T](implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = + new AttributeKey[T] { + def manifest = mf + def label = LocalLabel + def description = None + def extend = Nil + override def toString = label + def isLocal: Boolean = true + def rank = Int.MaxValue + val optJsonWriter = ojw + } + private[sbt] final val LocalLabel = "$" + "local" + } /** @@ -98,6 +119,7 @@ object AttributeKey { * Excluding this possibility is the responsibility of the client if desired. */ trait AttributeMap { + /** * Gets the value of type `T` associated with the key `k`. * If a key with the same label but different type is defined, this method will fail. @@ -142,8 +164,11 @@ trait AttributeMap { /** `true` if there are no mappings in this map, `false` if there are. */ def isEmpty: Boolean + } + object AttributeMap { + /** An [[AttributeMap]] without any mappings. */ val empty: AttributeMap = new BasicAttributeMap(Map.empty) @@ -157,27 +182,38 @@ object AttributeMap { implicit def toNatTrans(map: AttributeMap): AttributeKey ~> Id = new (AttributeKey ~> Id) { def apply[T](key: AttributeKey[T]): T = map(key) } + } -private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any]) extends AttributeMap { +private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any]) + extends AttributeMap { + def isEmpty: Boolean = backing.isEmpty def apply[T](k: AttributeKey[T]) = backing(k).asInstanceOf[T] def get[T](k: AttributeKey[T]) = backing.get(k).asInstanceOf[Option[T]] def remove[T](k: AttributeKey[T]): AttributeMap = new BasicAttributeMap(backing - k) def contains[T](k: AttributeKey[T]) = backing.contains(k) - def put[T](k: AttributeKey[T], value: T): AttributeMap = new BasicAttributeMap(backing.updated(k, value)) + + def put[T](k: AttributeKey[T], value: T): AttributeMap = + new BasicAttributeMap(backing.updated(k, value)) + def keys: Iterable[AttributeKey[_]] = backing.keys - def ++(o: Iterable[AttributeEntry[_]]): AttributeMap = - { - val newBacking = (backing /: o) { case (b, AttributeEntry(key, value)) => b.updated(key, value) } - new BasicAttributeMap(newBacking) + + def ++(o: Iterable[AttributeEntry[_]]): AttributeMap = { + val newBacking = (backing /: o) { + case (b, AttributeEntry(key, value)) => b.updated(key, value) } + new BasicAttributeMap(newBacking) + } + def ++(o: AttributeMap): AttributeMap = o match { case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing) case _ => o ++ this } + def entries: Iterable[AttributeEntry[_]] = for ((k: AttributeKey[kt], v) <- backing) yield AttributeEntry(k, v.asInstanceOf[kt]) + override def toString = entries.mkString("(", ", ", ")") } @@ -189,16 +225,21 @@ final case class AttributeEntry[T](key: AttributeKey[T], value: T) { /** Associates a `metadata` map with `data`. */ final case class Attributed[D](data: D)(val metadata: AttributeMap) { + /** Retrieves the associated value of `key` from the metadata. */ def get[T](key: AttributeKey[T]): Option[T] = metadata.get(key) /** Defines a mapping `key -> value` in the metadata. */ - def put[T](key: AttributeKey[T], value: T): Attributed[D] = Attributed(data)(metadata.put(key, value)) + def put[T](key: AttributeKey[T], value: T): Attributed[D] = + Attributed(data)(metadata.put(key, value)) /** Transforms the data by applying `f`. */ def map[T](f: D => T): Attributed[T] = Attributed(f(data))(metadata) + } + object Attributed { + /** Extracts the underlying data from the sequence `in`. */ def data[T](in: Seq[Attributed[T]]): Seq[T] = in.map(_.data) @@ -207,4 +248,5 @@ object Attributed { /** Associates an empty metadata map with `data`. */ def blank[T](data: T): Attributed[T] = Attributed(data)(AttributeMap.empty) + } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Classes.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Classes.scala index b44cb8606..bfef90b3e 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Classes.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Classes.scala @@ -6,19 +6,26 @@ object Classes { def pure[S](s: => S): M[S] def map[S, T](f: S => T, v: M[S]): M[T] } + trait Monad[M[_]] extends Applicative[M] { def flatten[T](m: M[M[T]]): M[T] } + implicit val optionMonad: Monad[Option] = new Monad[Option] { - def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match { case (Some(fv), Some(vv)) => Some(fv(vv)); case _ => None } + def apply[S, T](f: Option[S => T], v: Option[S]) = (f, v) match { + case (Some(fv), Some(vv)) => Some(fv(vv)) + case _ => None + } + def pure[S](s: => S) = Some(s) def map[S, T](f: S => T, v: Option[S]) = v map f def flatten[T](m: Option[Option[T]]): Option[T] = m.flatten } + implicit val listMonad: Monad[List] = new Monad[List] { def apply[S, T](f: List[S => T], v: List[S]) = for (fv <- f; vv <- v) yield fv(vv) def pure[S](s: => S) = s :: Nil def map[S, T](f: S => T, v: List[S]) = v map f def flatten[T](m: List[List[T]]): List[T] = m.flatten } -} \ No newline at end of file +} diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala index 1c9d93ea0..b9d58a0bd 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Dag.scala @@ -3,8 +3,7 @@ */ package sbt.internal.util -trait Dag[Node <: Dag[Node]] { - self: Node => +trait Dag[Node <: Dag[Node]] { self: Node => def dependencies: Iterable[Node] def topologicalSort = Dag.topologicalSort(self)(_.dependencies) @@ -13,53 +12,58 @@ object Dag { import scala.collection.{ mutable, JavaConverters } import JavaConverters.asScalaSetConverter - def topologicalSort[T](root: T)(dependencies: T => Iterable[T]): List[T] = topologicalSort(root :: Nil)(dependencies) + def topologicalSort[T](root: T)(dependencies: T => Iterable[T]): List[T] = + topologicalSort(root :: Nil)(dependencies) - def topologicalSort[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = - { - val discovered = new mutable.HashSet[T] - val finished = (new java.util.LinkedHashSet[T]).asScala + def topologicalSort[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = { + val discovered = new mutable.HashSet[T] + val finished = (new java.util.LinkedHashSet[T]).asScala - def visitAll(nodes: Iterable[T]) = nodes foreach visit - def visit(node: T): Unit = { - if (!discovered(node)) { - discovered(node) = true; - try { visitAll(dependencies(node)); } catch { case c: Cyclic => throw node :: c } - finished += node - () - } else if (!finished(node)) - throw new Cyclic(node) - } - - visitAll(nodes) - - finished.toList + def visitAll(nodes: Iterable[T]) = nodes foreach visit + def visit(node: T): Unit = { + if (!discovered(node)) { + discovered(node) = true; + try { visitAll(dependencies(node)); } catch { case c: Cyclic => throw node :: c } + finished += node + () + } else if (!finished(node)) + throw new Cyclic(node) } + + visitAll(nodes) + + finished.toList + } + // doesn't check for cycles - def topologicalSortUnchecked[T](node: T)(dependencies: T => Iterable[T]): List[T] = topologicalSortUnchecked(node :: Nil)(dependencies) + def topologicalSortUnchecked[T](node: T)(dependencies: T => Iterable[T]): List[T] = + topologicalSortUnchecked(node :: Nil)(dependencies) - def topologicalSortUnchecked[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = - { - val discovered = new mutable.HashSet[T] - var finished: List[T] = Nil + def topologicalSortUnchecked[T](nodes: Iterable[T])(dependencies: T => Iterable[T]): List[T] = { + val discovered = new mutable.HashSet[T] + var finished: List[T] = Nil - def visitAll(nodes: Iterable[T]) = nodes foreach visit - def visit(node: T): Unit = { - if (!discovered(node)) { - discovered(node) = true - visitAll(dependencies(node)) - finished ::= node - } + def visitAll(nodes: Iterable[T]) = nodes foreach visit + def visit(node: T): Unit = { + if (!discovered(node)) { + discovered(node) = true + visitAll(dependencies(node)) + finished ::= node } - - visitAll(nodes); - finished; } + + visitAll(nodes); + finished; + } + final class Cyclic(val value: Any, val all: List[Any], val complete: Boolean) - extends Exception("Cyclic reference involving " + - (if (complete) all.mkString("\n ", "\n ", "") else value)) { + extends Exception( + "Cyclic reference involving " + + (if (complete) all.mkString("\n ", "\n ", "") else value) + ) { def this(value: Any) = this(value, value :: Nil, false) override def toString = getMessage + def ::(a: Any): Cyclic = if (complete) this @@ -71,19 +75,25 @@ object Dag { /** A directed graph with edges labeled positive or negative. */ private[sbt] trait DirectedSignedGraph[Node] { + /** * Directed edge type that tracks the sign and target (head) vertex. * The sign can be obtained via [[isNegative]] and the target vertex via [[head]]. */ type Arrow + /** List of initial nodes. */ def nodes: List[Arrow] + /** Outgoing edges for `n`. */ def dependencies(n: Node): List[Arrow] + /** `true` if the edge `a` is "negative", false if it is "positive". */ def isNegative(a: Arrow): Boolean + /** The target of the directed edge `a`. */ def head(a: Arrow): Node + } /** @@ -92,36 +102,35 @@ object Dag { * If a cycle containing a "negative" edge is detected, its member edges are returned in order. * Otherwise, the empty list is returned. */ - private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] = - { - import graph._ - val finished = new mutable.HashSet[Node] - val visited = new mutable.HashSet[Node] + private[sbt] def findNegativeCycle[Node](graph: DirectedSignedGraph[Node]): List[graph.Arrow] = { + import graph._ + val finished = new mutable.HashSet[Node] + val visited = new mutable.HashSet[Node] - def visit(edges: List[Arrow], stack: List[Arrow]): List[Arrow] = edges match { - case Nil => Nil - case edge :: tail => - val node = head(edge) - if (!visited(node)) { - visited += node - visit(dependencies(node), edge :: stack) match { - case Nil => - finished += node - visit(tail, stack) - case cycle => cycle - } - } else if (!finished(node)) { - // cycle. If a negative edge is involved, it is an error. - val between = edge :: stack.takeWhile(f => head(f) != node) - if (between exists isNegative) - between - else + def visit(edges: List[Arrow], stack: List[Arrow]): List[Arrow] = edges match { + case Nil => Nil + case edge :: tail => + val node = head(edge) + if (!visited(node)) { + visited += node + visit(dependencies(node), edge :: stack) match { + case Nil => + finished += node visit(tail, stack) - } else + case cycle => cycle + } + } else if (!finished(node)) { + // cycle. If a negative edge is involved, it is an error. + val between = edge :: stack.takeWhile(f => head(f) != node) + if (between exists isNegative) + between + else visit(tail, stack) - } - - visit(graph.nodes, Nil) + } else + visit(tail, stack) } + visit(graph.nodes, Nil) + } + } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala index 37c19dfdc..384c4c0d2 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala @@ -12,13 +12,16 @@ import Types._ sealed trait HList { type Wrap[M[_]] <: HList } + sealed trait HNil extends HList { type Wrap[M[_]] = HNil def :+:[G](g: G): G :+: HNil = HCons(g, this) override def toString = "HNil" } + object HNil extends HNil + final case class HCons[H, T <: HList](head: H, tail: T) extends HList { type Wrap[M[_]] = M[H] :+: T#Wrap[M] def :+:[G](g: G): G :+: H :+: T = HCons(g, this) @@ -28,5 +31,6 @@ final case class HCons[H, T <: HList](head: H, tail: T) extends HList { object HList { // contains no type information: not even A - implicit def fromList[A](list: Traversable[A]): HList = ((HNil: HList) /: list)((hl, v) => HCons(v, hl)) -} \ No newline at end of file + implicit def fromList[A](list: Traversable[A]): HList = + ((HNil: HList) /: list)((hl, v) => HCons(v, hl)) +} diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/HListFormats.scala b/internal/util-collection/src/main/scala/sbt/internal/util/HListFormats.scala index 6abae921c..e8173f554 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/HListFormats.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/HListFormats.scala @@ -21,7 +21,10 @@ trait HListFormats { } } - implicit def hconsFormat[H, T <: HList](implicit hf: JsonFormat[H], tf: HListJF[T]): JsonFormat[H :+: T] = + implicit def hconsFormat[H, T <: HList]( + implicit hf: JsonFormat[H], + tf: HListJF[T] + ): JsonFormat[H :+: T] = new JsonFormat[H :+: T] { def write[J](hcons: H :+: T, builder: Builder[J]) = { builder.beginArray() @@ -34,7 +37,8 @@ trait HListFormats { case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder)) case Some(js) => unbuilder.beginArray(js) - val hcons = HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder)) + val hcons = + HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder)) unbuilder.endArray() hcons } @@ -45,7 +49,10 @@ trait HListFormats { def write[J](obj: A, builder: Builder[J]): Unit } - implicit def hconsHListJF[H, T <: HList](implicit hf: JsonFormat[H], tf: HListJF[T]): HListJF[H :+: T] = + implicit def hconsHListJF[H, T <: HList]( + implicit hf: JsonFormat[H], + tf: HListJF[T] + ): HListJF[H :+: T] = new HListJF[H :+: T] { def write[J](hcons: H :+: T, builder: Builder[J]) = { hf.write(hcons.head, builder) @@ -53,8 +60,9 @@ trait HListFormats { } def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]) = jsOpt match { - case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder)) - case Some(js) => HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder)) + case None => HCons(hf.read(None, unbuilder), tf.read(None, unbuilder)) + case Some(js) => + HCons(hf.read(Some(unbuilder.nextElement), unbuilder), tf.read(Some(js), unbuilder)) } } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala b/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala index d7a9f7c1a..df5b1dedc 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/IDSet.scala @@ -20,12 +20,13 @@ trait IDSet[T] { object IDSet { implicit def toTraversable[T]: IDSet[T] => Traversable[T] = _.all def apply[T](values: T*): IDSet[T] = apply(values) - def apply[T](values: Iterable[T]): IDSet[T] = - { - val s = create[T] - s ++= values - s - } + + def apply[T](values: Iterable[T]): IDSet[T] = { + val s = create[T] + s ++= values + s + } + def create[T]: IDSet[T] = new IDSet[T] { private[this] val backing = new java.util.IdentityHashMap[T, AnyRef] private[this] val Dummy: AnyRef = "" @@ -39,7 +40,10 @@ object IDSet { def all = collection.JavaConversions.collectionAsScalaIterable(backing.keySet) def toList = all.toList def isEmpty = backing.isEmpty - def process[S](t: T)(ifSeen: S)(ifNew: => S) = if (contains(t)) ifSeen else { this += t; ifNew } + + def process[S](t: T)(ifSeen: S)(ifNew: => S) = + if (contains(t)) ifSeen else { this += t; ifNew } + override def toString = backing.toString } } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala index 3c159ec11..6f1661cf5 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/INode.scala @@ -12,6 +12,7 @@ object EvaluationState extends Enumeration { abstract class EvaluateSettings[Scope] { protected val init: Init[Scope] import init._ + protected def executor: Executor protected def compiledSettings: Seq[Compiled[_]] @@ -20,23 +21,33 @@ abstract class EvaluateSettings[Scope] { private[this] val complete = new LinkedBlockingQueue[Option[Throwable]] private[this] val static = PMap.empty[ScopedKey, INode] private[this] val allScopes: Set[Scope] = compiledSettings.map(_.key.scope).toSet - private[this] def getStatic[T](key: ScopedKey[T]): INode[T] = static get key getOrElse sys.error("Illegal reference to key " + key) + + private[this] def getStatic[T](key: ScopedKey[T]): INode[T] = + static get key getOrElse sys.error("Illegal reference to key " + key) private[this] val transform: Initialize ~> INode = new (Initialize ~> INode) { def apply[T](i: Initialize[T]): INode[T] = i match { - case k: Keyed[s, T] @unchecked => single(getStatic(k.scopedKey), k.transform) - case a: Apply[k, T] @unchecked => new MixedNode[k, T](a.alist.transform[Initialize, INode](a.inputs, transform), a.f, a.alist) + case k: Keyed[s, T] @unchecked => single(getStatic(k.scopedKey), k.transform) + case a: Apply[k, T] @unchecked => + new MixedNode[k, T]( + a.alist.transform[Initialize, INode](a.inputs, transform), + a.f, + a.alist + ) case b: Bind[s, T] @unchecked => new BindNode[s, T](transform(b.in), x => transform(b.f(x))) case v: Value[T] @unchecked => constant(v.value) case v: ValidationCapture[T] @unchecked => strictConstant(v.key) case t: TransformCapture => strictConstant(t.f) - case o: Optional[s, T] @unchecked => o.a match { - case None => constant(() => o.f(None)) - case Some(i) => single[s, T](transform(i), x => o.f(Some(x))) - } - case x if x == StaticScopes => strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope] + case o: Optional[s, T] @unchecked => + o.a match { + case None => constant(() => o.f(None)) + case Some(i) => single[s, T](transform(i), x => o.f(Some(x))) + } + case x if x == StaticScopes => + strictConstant(allScopes.asInstanceOf[T]) // can't convince scalac that StaticScopes => T == Set[Scope] } } + private[this] lazy val roots: Seq[INode[_]] = compiledSettings flatMap { cs => (cs.settings map { s => val t = transform(s.init) @@ -44,42 +55,47 @@ abstract class EvaluateSettings[Scope] { t }): Seq[INode[_]] } + private[this] var running = new AtomicInteger private[this] var cancel = new AtomicBoolean(false) - def run(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = - { - assert(running.get() == 0, "Already running") - startWork() - roots.foreach(_.registerIfNew()) - workComplete() - complete.take() foreach { ex => - cancel.set(true) - throw ex - } - getResults(delegates) + def run(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = { + assert(running.get() == 0, "Already running") + startWork() + roots.foreach(_.registerIfNew()) + workComplete() + complete.take() foreach { ex => + cancel.set(true) + throw ex } + getResults(delegates) + } + private[this] def getResults(implicit delegates: Scope => Seq[Scope]) = (empty /: static.toTypedSeq) { case (ss, static.TPair(key, node)) => if (key.key.isLocal) ss else ss.set(key.scope, key.key, node.get) } + private[this] val getValue = new (INode ~> Id) { def apply[T](node: INode[T]) = node.get } private[this] def submitEvaluate(node: INode[_]) = submit(node.evaluate()) - private[this] def submitCallComplete[T](node: BindNode[_, T], value: T) = submit(node.callComplete(value)) - private[this] def submit(work: => Unit): Unit = - { - startWork() - executor.execute(new Runnable { def run = if (!cancel.get()) run0(work) }) - } - private[this] def run0(work: => Unit): Unit = - { - try { work } catch { case e: Throwable => complete.put(Some(e)) } - workComplete() - } + + private[this] def submitCallComplete[T](node: BindNode[_, T], value: T) = + submit(node.callComplete(value)) + + private[this] def submit(work: => Unit): Unit = { + startWork() + executor.execute(new Runnable { def run = if (!cancel.get()) run0(work) }) + } + + private[this] def run0(work: => Unit): Unit = { + try { work } catch { case e: Throwable => complete.put(Some(e)) } + workComplete() + } private[this] def startWork(): Unit = { running.incrementAndGet(); () } + private[this] def workComplete(): Unit = if (running.decrementAndGet() == 0) complete.put(None) @@ -91,26 +107,32 @@ abstract class EvaluateSettings[Scope] { private[this] var blockedOn: Int = 0 private[this] val calledBy = new collection.mutable.ListBuffer[BindNode[_, T]] - override def toString = getClass.getName + " (state=" + state + ",blockedOn=" + blockedOn + ",calledBy=" + calledBy.size + ",blocking=" + blocking.size + "): " + - keyString + override def toString = + getClass.getName + " (state=" + state + ",blockedOn=" + blockedOn + ",calledBy=" + calledBy.size + ",blocking=" + blocking.size + "): " + + keyString private[this] def keyString = - (static.toSeq.flatMap { case (key, value) => if (value eq this) init.showFullKey.show(key) :: Nil else Nil }).headOption getOrElse "non-static" + (static.toSeq.flatMap { + case (key, value) => if (value eq this) init.showFullKey.show(key) :: Nil else Nil + }).headOption getOrElse "non-static" final def get: T = synchronized { assert(value != null, toString + " not evaluated") value } + final def doneOrBlock(from: INode[_]): Boolean = synchronized { val ready = state == Evaluated if (!ready) blocking += from registerIfNew() ready } + final def isDone: Boolean = synchronized { state == Evaluated } final def isNew: Boolean = synchronized { state == New } final def isCalling: Boolean = synchronized { state == Calling } final def registerIfNew(): Unit = synchronized { if (state == New) register() } + private[this] def register(): Unit = { assert(state == New, "Already registered and: " + toString) val deps = dependsOn @@ -126,28 +148,36 @@ abstract class EvaluateSettings[Scope] { state = Ready submitEvaluate(this) } + final def unblocked(): Unit = synchronized { assert(state == Blocked, "Invalid state for unblocked() call: " + toString) blockedOn -= 1 assert(blockedOn >= 0, "Negative blockedOn: " + blockedOn + " for " + toString) if (blockedOn == 0) schedule() } + final def evaluate(): Unit = synchronized { evaluate0() } + protected final def makeCall(source: BindNode[_, T], target: INode[T]): Unit = { assert(state == Ready, "Invalid state for call to makeCall: " + toString) state = Calling target.call(source) } + protected final def setValue(v: T): Unit = { - assert(state != Evaluated, "Already evaluated (trying to set value to " + v + "): " + toString) + assert(state != Evaluated, + "Already evaluated (trying to set value to " + v + "): " + toString) if (v == null) sys.error("Setting value cannot be null: " + keyString) value = v state = Evaluated blocking foreach { _.unblocked() } blocking.clear() - calledBy foreach { node => submitCallComplete(node, value) } + calledBy foreach { node => + submitCallComplete(node, value) + } calledBy.clear() } + final def call(by: BindNode[_, T]): Unit = synchronized { registerIfNew() state match { @@ -156,13 +186,19 @@ abstract class EvaluateSettings[Scope] { } () } + protected def dependsOn: Seq[INode[_]] protected def evaluate0(): Unit } private[this] def strictConstant[T](v: T): INode[T] = constant(() => v) - private[this] def constant[T](f: () => T): INode[T] = new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty) - private[this] def single[S, T](in: INode[S], f: S => T): INode[T] = new MixedNode[({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S]) + + private[this] def constant[T](f: () => T): INode[T] = + new MixedNode[ConstK[Unit]#l, T]((), _ => f(), AList.empty) + + private[this] def single[S, T](in: INode[S], f: S => T): INode[T] = + new MixedNode[({ type l[L[x]] = L[S] })#l, T](in, f, AList.single[S]) + private[this] final class BindNode[S, T](in: INode[S], f: S => INode[T]) extends INode[T] { protected def dependsOn = in :: Nil protected def evaluate0(): Unit = makeCall(this, f(in.get)) @@ -171,7 +207,9 @@ abstract class EvaluateSettings[Scope] { setValue(value) } } - private[this] final class MixedNode[K[L[x]], T](in: K[INode], f: K[Id] => T, alist: AList[K]) extends INode[T] { + + private[this] final class MixedNode[K[L[x]], T](in: K[INode], f: K[Id] => T, alist: AList[K]) + extends INode[T] { protected def dependsOn = alist.toList(in) protected def evaluate0(): Unit = setValue(f(alist.transform(in, getValue))) } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala index 3406f1b4b..5e470c413 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/KList.scala @@ -22,32 +22,38 @@ sealed trait KList[+M[_]] { /** Discards the heterogeneous type information and constructs a plain List from this KList's elements. */ def toList: List[M[_]] } + final case class KCons[H, +T <: KList[M], +M[_]](head: M[H], tail: T) extends KList[M] { final type Transform[N[_]] = KCons[H, tail.Transform[N], N] def transform[N[_]](f: M ~> N) = KCons(f(head), tail.transform(f)) def toList: List[M[_]] = head :: tail.toList - def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] = - { - val g = (t: tail.Transform[Id]) => (h: H) => f(KCons[H, tail.Transform[Id], Id](h, t)) - ap.apply(tail.apply[N, H => Z](g), head) - } - def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] = - { - val tt: N[tail.Transform[P]] = tail.traverse[N, P](f) - val g = (t: tail.Transform[P]) => (h: P[H]) => KCons(h, t) - np.apply(np.map(g, tt), f(head)) - } + + def apply[N[x] >: M[x], Z](f: Transform[Id] => Z)(implicit ap: Applicative[N]): N[Z] = { + val g = (t: tail.Transform[Id]) => (h: H) => f(KCons[H, tail.Transform[Id], Id](h, t)) + ap.apply(tail.apply[N, H => Z](g), head) + } + + def traverse[N[_], P[_]](f: M ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[Transform[P]] = { + val tt: N[tail.Transform[P]] = tail.traverse[N, P](f) + val g = (t: tail.Transform[P]) => (h: P[H]) => KCons(h, t) + np.apply(np.map(g, tt), f(head)) + } + def :^:[A, N[x] >: M[x]](h: N[A]) = KCons(h, this) override def foldr[B](f: (M[_], B) => B, init: B): B = f(head, tail.foldr(f, init)) } + sealed abstract class KNil extends KList[Nothing] { final type Transform[N[_]] = KNil final def transform[N[_]](f: Nothing ~> N): Transform[N] = KNil final def toList = Nil final def apply[N[x], Z](f: KNil => Z)(implicit ap: Applicative[N]): N[Z] = ap.pure(f(KNil)) - final def traverse[N[_], P[_]](f: Nothing ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] = np.pure(KNil) + + final def traverse[N[_], P[_]](f: Nothing ~> (N ∙ P)#l)(implicit np: Applicative[N]): N[KNil] = + np.pure(KNil) } + case object KNil extends KNil { def :^:[M[_], H](h: M[H]): KCons[H, KNil, M] = KCons(h, this) } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala b/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala index 989d657e2..4fe6f51ca 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/PMap.scala @@ -10,7 +10,11 @@ trait RMap[K[_], V[_]] { def get[T](k: K[T]): Option[V[T]] def contains[T](k: K[T]): Boolean def toSeq: Seq[(K[_], V[_])] - def toTypedSeq: Seq[TPair[_]] = toSeq.map { case (k: K[t], v) => TPair[t](k, v.asInstanceOf[V[t]]) } + + def toTypedSeq: Seq[TPair[_]] = toSeq.map { + case (k: K[t], v) => TPair[t](k, v.asInstanceOf[V[t]]) + } + def keys: Iterable[K[_]] def values: Iterable[V[_]] def isEmpty: Boolean @@ -23,19 +27,24 @@ trait IMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { def remove[T](k: K[T]): IMap[K, V] def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): IMap[K, V] def mapValues[V2[_]](f: V ~> V2): IMap[K, V2] - def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l): (IMap[K, VL], IMap[K, VR]) + def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l) + : (IMap[K, VL], IMap[K, VR]) } + trait PMap[K[_], V[_]] extends (K ~> V) with RMap[K, V] { def update[T](k: K[T], v: V[T]): Unit def remove[T](k: K[T]): Option[V[T]] def getOrUpdate[T](k: K[T], make: => V[T]): V[T] def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] } + object PMap { implicit def toFunction[K[_], V[_]](map: PMap[K, V]): K[_] => V[_] = k => map(k) def empty[K[_], V[_]]: PMap[K, V] = new DelegatingPMap[K, V](new mutable.HashMap) } + object IMap { + /** * Only suitable for K that is invariant in its type parameter. * Option and List keys are not suitable, for example, @@ -43,7 +52,9 @@ object IMap { */ def empty[K[_], V[_]]: IMap[K, V] = new IMap0[K, V](Map.empty) - private[this] class IMap0[K[_], V[_]](backing: Map[K[_], V[_]]) extends AbstractRMap[K, V] with IMap[K, V] { + private[this] class IMap0[K[_], V[_]](backing: Map[K[_], V[_]]) + extends AbstractRMap[K, V] + with IMap[K, V] { def get[T](k: K[T]): Option[V[T]] = (backing get k).asInstanceOf[Option[V[T]]] def put[T](k: K[T], v: V[T]) = new IMap0[K, V](backing.updated(k, v)) def remove[T](k: K[T]) = new IMap0[K, V](backing - k) @@ -54,17 +65,17 @@ object IMap { def mapValues[V2[_]](f: V ~> V2) = new IMap0[K, V2](backing.mapValues(x => f(x))) - def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l) = - { - val mapped = backing.iterator.map { - case (k, v) => f(v) match { + def mapSeparate[VL[_], VR[_]](f: V ~> ({ type l[T] = Either[VL[T], VR[T]] })#l) = { + val mapped = backing.iterator.map { + case (k, v) => + f(v) match { case Left(l) => Left((k, l)) case Right(r) => Right((k, r)) } - } - val (l, r) = Util.separateE[(K[_], VL[_]), (K[_], VR[_])](mapped.toList) - (new IMap0[K, VL](l.toMap), new IMap0[K, VR](r.toMap)) } + val (l, r) = Util.separateE[(K[_], VL[_]), (K[_], VR[_])](mapped.toList) + (new IMap0[K, VL](l.toMap), new IMap0[K, VR](r.toMap)) + } def toSeq = backing.toSeq def keys = backing.keys @@ -85,17 +96,20 @@ abstract class AbstractRMap[K[_], V[_]] extends RMap[K, V] { * Option and List keys are not suitable, for example, * because None <:< Option[String] and None <: Option[Int]. */ -class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]]) extends AbstractRMap[K, V] with PMap[K, V] { +class DelegatingPMap[K[_], V[_]](backing: mutable.Map[K[_], V[_]]) + extends AbstractRMap[K, V] + with PMap[K, V] { def get[T](k: K[T]): Option[V[T]] = cast[T](backing.get(k)) def update[T](k: K[T], v: V[T]): Unit = { backing(k) = v } def remove[T](k: K[T]) = cast(backing.remove(k)) def getOrUpdate[T](k: K[T], make: => V[T]) = cast[T](backing.getOrElseUpdate(k, make)) - def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] = - { - val v = f(this get k getOrElse init) - update(k, v) - v - } + + def mapValue[T](k: K[T], init: V[T], f: V[T] => V[T]): V[T] = { + val v = f(this get k getOrElse init) + update(k, v) + v + } + def toSeq = backing.toSeq def keys = backing.keys def values = backing.values diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index afa02c150..2be4f4b88 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -19,30 +19,40 @@ sealed trait Settings[Scope] { def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] } -private final class Settings0[Scope](val data: Map[Scope, AttributeMap], val delegates: Scope => Seq[Scope]) extends Settings[Scope] { +private final class Settings0[Scope]( + val data: Map[Scope, AttributeMap], + val delegates: Scope => Seq[Scope] +) extends Settings[Scope] { + def scopes: Set[Scope] = data.keySet def keys(scope: Scope) = data(scope).keys.toSet - def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] = data.flatMap { case (scope, map) => map.keys.map(k => f(scope, k)) }.toSeq + + def allKeys[T](f: (Scope, AttributeKey[_]) => T): Seq[T] = + data.flatMap { case (scope, map) => map.keys.map(k => f(scope, k)) }.toSeq def get[T](scope: Scope, key: AttributeKey[T]): Option[T] = delegates(scope).toStream.flatMap(sc => getDirect(sc, key)).headOption + def definingScope(scope: Scope, key: AttributeKey[_]): Option[Scope] = delegates(scope).toStream.find(sc => getDirect(sc, key).isDefined) def getDirect[T](scope: Scope, key: AttributeKey[T]): Option[T] = (data get scope).flatMap(_ get key) - def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] = - { - val map = data getOrElse (scope, AttributeMap.empty) - val newData = data.updated(scope, map.put(key, value)) - new Settings0(newData, delegates) - } + def set[T](scope: Scope, key: AttributeKey[T], value: T): Settings[Scope] = { + val map = data getOrElse (scope, AttributeMap.empty) + val newData = data.updated(scope, map.put(key, value)) + new Settings0(newData, delegates) + } } + // delegates should contain the input Scope as the first entry // this trait is intended to be mixed into an object trait Init[Scope] { - /** The Show instance used when a detailed String needs to be generated. It is typically used when no context is available.*/ + + /** The Show instance used when a detailed String needs to be generated. + * It is typically used when no context is available. + */ def showFullKey: Show[ScopedKey[_]] sealed case class ScopedKey[T](scope: Scope, key: AttributeKey[T]) extends KeyedInitialize[T] { @@ -67,17 +77,32 @@ trait Init[Scope] { * The result of this initialization is the composition of applied transformations. * This can be useful when dealing with dynamic Initialize values. */ - lazy val capturedTransformations: Initialize[Initialize ~> Initialize] = new TransformCapture(idK[Initialize]) + lazy val capturedTransformations: Initialize[Initialize ~> Initialize] = + new TransformCapture(idK[Initialize]) + + def setting[T]( + key: ScopedKey[T], + init: Initialize[T], + pos: SourcePosition = NoPosition + ): Setting[T] = new Setting[T](key, init, pos) - def setting[T](key: ScopedKey[T], init: Initialize[T], pos: SourcePosition = NoPosition): Setting[T] = new Setting[T](key, init, pos) def valueStrict[T](value: T): Initialize[T] = pure(() => value) def value[T](value: => T): Initialize[T] = pure(value _) def pure[T](value: () => T): Initialize[T] = new Value(value) def optional[T, U](i: Initialize[T])(f: Option[T] => U): Initialize[U] = new Optional(Some(i), f) - def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = setting[T](key, map(key)(f), NoPosition) + + def update[T](key: ScopedKey[T])(f: T => T): Setting[T] = + setting[T](key, map(key)(f), NoPosition) + def bind[S, T](in: Initialize[S])(f: S => Initialize[T]): Initialize[T] = new Bind(f, in) - def map[S, T](in: Initialize[S])(f: S => T): Initialize[T] = new Apply[({ type l[L[x]] = L[S] })#l, T](f, in, AList.single[S]) - def app[K[L[x]], T](inputs: K[Initialize])(f: K[Id] => T)(implicit alist: AList[K]): Initialize[T] = new Apply[K, T](f, inputs, alist) + + def map[S, T](in: Initialize[S])(f: S => T): Initialize[T] = + new Apply[({ type l[L[x]] = L[S] })#l, T](f, in, AList.single[S]) + + def app[K[L[x]], T](inputs: K[Initialize])(f: K[Id] => T)( + implicit alist: AList[K] + ): Initialize[T] = new Apply[K, T](f, inputs, alist) + def uniform[S, T](inputs: Seq[Initialize[S]])(f: Seq[S] => T): Initialize[T] = new Apply[({ type l[L[x]] = List[L[S]] })#l, T](f, inputs.toList, AList.seq[S]) @@ -86,7 +111,11 @@ trait Init[Scope] { * No dependency is introduced on `key`. If `selfRefOk` is true, validation will not fail if the key is referenced by a definition of `key`. * That is, key := f(validated(key).value) is allowed only if `selfRefOk == true`. */ - private[sbt] final def validated[T](key: ScopedKey[T], selfRefOk: Boolean): ValidationCapture[T] = new ValidationCapture(key, selfRefOk) + private[sbt] final def validated[T]( + key: ScopedKey[T], + selfRefOk: Boolean + ): ValidationCapture[T] = + new ValidationCapture(key, selfRefOk) /** * Constructs a derived setting that will be automatically defined in every scope where one of its dependencies @@ -94,7 +123,13 @@ trait Init[Scope] { * A setting initialized with dynamic dependencies is only allowed if `allowDynamic` is true. * Only the static dependencies are tracked, however. Dependencies on previous values do not introduce a derived setting either. */ - final def derive[T](s: Setting[T], allowDynamic: Boolean = false, filter: Scope => Boolean = const(true), trigger: AttributeKey[_] => Boolean = const(true), default: Boolean = false): Setting[T] = { + final def derive[T]( + s: Setting[T], + allowDynamic: Boolean = false, + filter: Scope => Boolean = const(true), + trigger: AttributeKey[_] => Boolean = const(true), + default: Boolean = false + ): Setting[T] = { deriveAllowed(s, allowDynamic) foreach sys.error val d = new DerivedSetting[T](s.key, s.init, s.pos, filter, trigger) if (default) d.default() else d @@ -107,49 +142,73 @@ trait Init[Scope] { // id is used for equality private[sbt] final def defaultSetting[T](s: Setting[T]): Setting[T] = s.default() - private[sbt] def defaultSettings(ss: Seq[Setting[_]]): Seq[Setting[_]] = ss.map(s => defaultSetting(s)) + + private[sbt] def defaultSettings(ss: Seq[Setting[_]]): Seq[Setting[_]] = + ss.map(s => defaultSetting(s)) + private[this] final val nextID = new java.util.concurrent.atomic.AtomicLong private[this] final def nextDefaultID(): Long = nextID.incrementAndGet() - def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = new Settings0(Map.empty, delegates) + def empty(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = + new Settings0(Map.empty, delegates) + def asTransform(s: Settings[Scope]): ScopedKey ~> Id = new (ScopedKey ~> Id) { def apply[T](k: ScopedKey[T]): T = getValue(s, k) } - def getValue[T](s: Settings[Scope], k: ScopedKey[T]) = s.get(k.scope, k.key) getOrElse (throw new InvalidReference(k)) + + def getValue[T](s: Settings[Scope], k: ScopedKey[T]) = + s.get(k.scope, k.key) getOrElse (throw new InvalidReference(k)) + def asFunction[T](s: Settings[Scope]): ScopedKey[T] => T = k => getValue(s, k) + def mapScope(f: Scope => Scope): MapScoped = new MapScoped { def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope)) } - private final class InvalidReference(val key: ScopedKey[_]) extends RuntimeException("Internal settings error: invalid reference to " + showFullKey.show(key)) - private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] = - { - val (defaults, others) = Util.separate[Setting[_], DefaultSetting[_], Setting[_]](ss) { case u: DefaultSetting[_] => Left(u); case s => Right(s) } - defaults.distinct ++ others - } + private final class InvalidReference(val key: ScopedKey[_]) + extends RuntimeException( + "Internal settings error: invalid reference to " + showFullKey.show(key) + ) - def compiled(init: Seq[Setting[_]], actual: Boolean = true)(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): CompiledMap = - { - val initDefaults = applyDefaults(init) - // inject derived settings into scopes where their dependencies are directly defined - // and prepend per-scope settings - val derived = deriveAndLocal(initDefaults) - // group by Scope/Key, dropping dead initializations - val sMap: ScopedMap = grouped(derived) - // delegate references to undefined values according to 'delegates' - val dMap: ScopedMap = if (actual) delegate(sMap)(delegates, display) else sMap - // merge Seq[Setting[_]] into Compiled - compile(dMap) + private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] = { + val (defaults, others) = Util.separate[Setting[_], DefaultSetting[_], Setting[_]](ss) { + case u: DefaultSetting[_] => Left(u); case s => Right(s) } - def make(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal, display: Show[ScopedKey[_]]): Settings[Scope] = - { - val cMap = compiled(init)(delegates, scopeLocal, display) - // order the initializations. cyclic references are detected here. - val ordered: Seq[Compiled[_]] = sort(cMap) - // evaluation: apply the initializations. - try { applyInits(ordered) } - catch { case rru: RuntimeUndefined => throw Uninitialized(cMap.keys.toSeq, delegates, rru.undefined, true) } + defaults.distinct ++ others + } + + def compiled(init: Seq[Setting[_]], actual: Boolean = true)( + implicit delegates: Scope => Seq[Scope], + scopeLocal: ScopeLocal, + display: Show[ScopedKey[_]] + ): CompiledMap = { + val initDefaults = applyDefaults(init) + // inject derived settings into scopes where their dependencies are directly defined + // and prepend per-scope settings + val derived = deriveAndLocal(initDefaults) + // group by Scope/Key, dropping dead initializations + val sMap: ScopedMap = grouped(derived) + // delegate references to undefined values according to 'delegates' + val dMap: ScopedMap = if (actual) delegate(sMap)(delegates, display) else sMap + // merge Seq[Setting[_]] into Compiled + compile(dMap) + } + + def make(init: Seq[Setting[_]])( + implicit delegates: Scope => Seq[Scope], + scopeLocal: ScopeLocal, + display: Show[ScopedKey[_]] + ): Settings[Scope] = { + val cMap = compiled(init)(delegates, scopeLocal, display) + // order the initializations. cyclic references are detected here. + val ordered: Seq[Compiled[_]] = sort(cMap) + // evaluation: apply the initializations. + try { applyInits(ordered) } catch { + case rru: RuntimeUndefined => + throw Uninitialized(cMap.keys.toSeq, delegates, rru.undefined, true) } + } + def sort(cMap: CompiledMap): Seq[Compiled[_]] = Dag.topologicalSort(cMap.values)(_.dependencies.map(cMap)) @@ -172,237 +231,316 @@ trait Init[Scope] { def addLocal(init: Seq[Setting[_]])(implicit scopeLocal: ScopeLocal): Seq[Setting[_]] = init.flatMap(_.dependencies flatMap scopeLocal) ++ init - def delegate(sMap: ScopedMap)(implicit delegates: Scope => Seq[Scope], display: Show[ScopedKey[_]]): ScopedMap = - { - def refMap(ref: Setting[_], isFirst: Boolean) = new ValidateKeyRef { - def apply[T](k: ScopedKey[T], selfRefOk: Boolean) = - delegateForKey(sMap, k, delegates(k.scope), ref, selfRefOk || !isFirst) - } - type ValidatedSettings[T] = Either[Seq[Undefined], SettingSeq[T]] - val f = new (SettingSeq ~> ValidatedSettings) { - def apply[T](ks: Seq[Setting[T]]) = { - val (undefs, valid) = Util.separate(ks.zipWithIndex) { case (s, i) => s validateKeyReferenced refMap(s, i == 0) } - if (undefs.isEmpty) Right(valid) else Left(undefs.flatten) - } - } - type Undefs[_] = Seq[Undefined] - val (undefineds, result) = sMap.mapSeparate[Undefs, SettingSeq](f) - if (undefineds.isEmpty) - result - else - throw Uninitialized(sMap.keys.toSeq, delegates, undefineds.values.flatten.toList, false) - } - private[this] def delegateForKey[T](sMap: ScopedMap, k: ScopedKey[T], scopes: Seq[Scope], ref: Setting[_], selfRefOk: Boolean): Either[Undefined, ScopedKey[T]] = - { - val skeys = scopes.iterator.map(x => ScopedKey(x, k.key)) - val definedAt = skeys.find(sk => (selfRefOk || ref.key != sk) && (sMap contains sk)) - definedAt.toRight(Undefined(ref, k)) + def delegate(sMap: ScopedMap)( + implicit delegates: Scope => Seq[Scope], + display: Show[ScopedKey[_]] + ): ScopedMap = { + def refMap(ref: Setting[_], isFirst: Boolean) = new ValidateKeyRef { + def apply[T](k: ScopedKey[T], selfRefOk: Boolean) = + delegateForKey(sMap, k, delegates(k.scope), ref, selfRefOk || !isFirst) } - private[this] def applyInits(ordered: Seq[Compiled[_]])(implicit delegates: Scope => Seq[Scope]): Settings[Scope] = - { - val x = java.util.concurrent.Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors) - try { - val eval: EvaluateSettings[Scope] = new EvaluateSettings[Scope] { - override val init: Init.this.type = Init.this - def compiledSettings = ordered - def executor = x + type ValidatedSettings[T] = Either[Seq[Undefined], SettingSeq[T]] + + val f = new (SettingSeq ~> ValidatedSettings) { + def apply[T](ks: Seq[Setting[T]]) = { + val (undefs, valid) = Util.separate(ks.zipWithIndex) { + case (s, i) => s validateKeyReferenced refMap(s, i == 0) } - eval.run - } finally { x.shutdown() } + if (undefs.isEmpty) Right(valid) else Left(undefs.flatten) + } } - def showUndefined(u: Undefined, validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope])(implicit display: Show[ScopedKey[_]]): String = - { - val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey) - val derived = u.defining.isDerived - val refString = display.show(u.defining.key) - val sourceString = if (derived) "" else parenPosString(u.defining) - val guessedString = if (derived) "" else guessed.map(g => "\n Did you mean " + display.show(g) + " ?").toList.mkString - val derivedString = if (derived) ", which is a derived setting that needs this key to be defined in this scope." else "" - display.show(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString - } + type Undefs[_] = Seq[Undefined] + val (undefineds, result) = sMap.mapSeparate[Undefs, SettingSeq](f) + + if (undefineds.isEmpty) + result + else + throw Uninitialized(sMap.keys.toSeq, delegates, undefineds.values.flatten.toList, false) + } + + private[this] def delegateForKey[T]( + sMap: ScopedMap, + k: ScopedKey[T], + scopes: Seq[Scope], + ref: Setting[_], + selfRefOk: Boolean + ): Either[Undefined, ScopedKey[T]] = { + val skeys = scopes.iterator.map(x => ScopedKey(x, k.key)) + val definedAt = skeys.find(sk => (selfRefOk || ref.key != sk) && (sMap contains sk)) + definedAt.toRight(Undefined(ref, k)) + } + + private[this] def applyInits(ordered: Seq[Compiled[_]])( + implicit delegates: Scope => Seq[Scope] + ): Settings[Scope] = { + val x = + java.util.concurrent.Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors) + try { + val eval: EvaluateSettings[Scope] = new EvaluateSettings[Scope] { + override val init: Init.this.type = Init.this + def compiledSettings = ordered + def executor = x + } + eval.run + } finally { x.shutdown() } + } + + def showUndefined(u: Undefined, validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope])( + implicit display: Show[ScopedKey[_]] + ): String = { + val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey) + val derived = u.defining.isDerived + val refString = display.show(u.defining.key) + val sourceString = if (derived) "" else parenPosString(u.defining) + val guessedString = + if (derived) "" + else guessed.map(g => "\n Did you mean " + display.show(g) + " ?").toList.mkString + val derivedString = + if (derived) ", which is a derived setting that needs this key to be defined in this scope." + else "" + display.show(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString + } + private[this] def parenPosString(s: Setting[_]): String = s.positionString match { case None => ""; case Some(s) => " (" + s + ")" } - def guessIntendedScope(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], key: ScopedKey[_]): Option[ScopedKey[_]] = - { - val distances = validKeys.flatMap { validKey => refinedDistance(delegates, validKey, key).map(dist => (dist, validKey)) } - distances.sortBy(_._1).map(_._2).headOption + def guessIntendedScope( + validKeys: Seq[ScopedKey[_]], + delegates: Scope => Seq[Scope], + key: ScopedKey[_] + ): Option[ScopedKey[_]] = { + val distances = validKeys.flatMap { validKey => + refinedDistance(delegates, validKey, key).map(dist => (dist, validKey)) } - def refinedDistance(delegates: Scope => Seq[Scope], a: ScopedKey[_], b: ScopedKey[_]): Option[Int] = + distances.sortBy(_._1).map(_._2).headOption + } + + def refinedDistance( + delegates: Scope => Seq[Scope], + a: ScopedKey[_], + b: ScopedKey[_] + ): Option[Int] = if (a.key != b.key || a == b) None else { val dist = delegates(a.scope).indexOf(b.scope) if (dist < 0) None else Some(dist) } - final class Uninitialized(val undefined: Seq[Undefined], override val toString: String) extends Exception(toString) + final class Uninitialized(val undefined: Seq[Undefined], override val toString: String) + extends Exception(toString) + final class Undefined private[sbt] (val defining: Setting[_], val referencedKey: ScopedKey[_]) - final class RuntimeUndefined(val undefined: Seq[Undefined]) extends RuntimeException("References to undefined settings at runtime.") { + + final class RuntimeUndefined(val undefined: Seq[Undefined]) + extends RuntimeException("References to undefined settings at runtime.") { override def getMessage = super.getMessage + undefined.map { u => "\n" + u.defining + " referenced from " + u.referencedKey }.mkString } - def Undefined(defining: Setting[_], referencedKey: ScopedKey[_]): Undefined = new Undefined(defining, referencedKey) - def Uninitialized(validKeys: Seq[ScopedKey[_]], delegates: Scope => Seq[Scope], keys: Seq[Undefined], runtime: Boolean)(implicit display: Show[ScopedKey[_]]): Uninitialized = - { - assert(keys.nonEmpty) - val suffix = if (keys.length > 1) "s" else "" - val prefix = if (runtime) "Runtime reference" else "Reference" - val keysString = keys.map(u => showUndefined(u, validKeys, delegates)).mkString("\n\n ", "\n\n ", "") - new Uninitialized(keys, prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ") - } - final class Compiled[T](val key: ScopedKey[T], val dependencies: Iterable[ScopedKey[_]], val settings: Seq[Setting[T]]) { + def Undefined(defining: Setting[_], referencedKey: ScopedKey[_]): Undefined = + new Undefined(defining, referencedKey) + + def Uninitialized( + validKeys: Seq[ScopedKey[_]], + delegates: Scope => Seq[Scope], + keys: Seq[Undefined], + runtime: Boolean + )(implicit display: Show[ScopedKey[_]]): Uninitialized = { + assert(keys.nonEmpty) + val suffix = if (keys.length > 1) "s" else "" + val prefix = if (runtime) "Runtime reference" else "Reference" + val keysString = + keys.map(u => showUndefined(u, validKeys, delegates)).mkString("\n\n ", "\n\n ", "") + new Uninitialized( + keys, + prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ") + } + + final class Compiled[T]( + val key: ScopedKey[T], + val dependencies: Iterable[ScopedKey[_]], + val settings: Seq[Setting[T]] + ) { override def toString = showFullKey.show(key) } + final class Flattened(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]]) - def flattenLocals(compiled: CompiledMap): Map[ScopedKey[_], Flattened] = - { - val locals = compiled flatMap { case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil } - val ordered = Dag.topologicalSort(locals)(_.dependencies.flatMap(dep => if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil)) - def flatten(cmap: Map[ScopedKey[_], Flattened], key: ScopedKey[_], deps: Iterable[ScopedKey[_]]): Flattened = - new Flattened(key, deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil)) + def flattenLocals(compiled: CompiledMap): Map[ScopedKey[_], Flattened] = { + val locals = compiled flatMap { + case (key, comp) => if (key.key.isLocal) Seq[Compiled[_]](comp) else Nil + } + val ordered = Dag.topologicalSort(locals)(_.dependencies.flatMap(dep => + if (dep.key.isLocal) Seq[Compiled[_]](compiled(dep)) else Nil)) + def flatten( + cmap: Map[ScopedKey[_], Flattened], + key: ScopedKey[_], + deps: Iterable[ScopedKey[_]] + ): Flattened = + new Flattened( + key, + deps.flatMap(dep => if (dep.key.isLocal) cmap(dep).dependencies else dep :: Nil)) - val empty = Map.empty[ScopedKey[_], Flattened] - val flattenedLocals = (empty /: ordered) { (cmap, c) => cmap.updated(c.key, flatten(cmap, c.key, c.dependencies)) } - compiled flatMap { - case (key, comp) => - if (key.key.isLocal) - Nil - else - Seq[(ScopedKey[_], Flattened)]((key, flatten(flattenedLocals, key, comp.dependencies))) - } + val empty = Map.empty[ScopedKey[_], Flattened] + + val flattenedLocals = (empty /: ordered) { (cmap, c) => + cmap.updated(c.key, flatten(cmap, c.key, c.dependencies)) } - def definedAtString(settings: Seq[Setting[_]]): String = - { - val posDefined = settings.flatMap(_.positionString.toList) - if (posDefined.nonEmpty) { - val header = if (posDefined.size == settings.size) "defined at:" else + compiled flatMap { + case (key, comp) => + if (key.key.isLocal) + Nil + else + Seq[(ScopedKey[_], Flattened)]((key, flatten(flattenedLocals, key, comp.dependencies))) + } + } + + def definedAtString(settings: Seq[Setting[_]]): String = { + val posDefined = settings.flatMap(_.positionString.toList) + if (posDefined.nonEmpty) { + val header = + if (posDefined.size == settings.size) "defined at:" + else "some of the defining occurrences:" - header + (posDefined.distinct mkString ("\n\t", "\n\t", "\n")) - } else "" - } + header + (posDefined.distinct mkString ("\n\t", "\n\t", "\n")) + } else "" + } /** * Intersects two scopes, returning the more specific one if they intersect, or None otherwise. */ - private[sbt] def intersect(s1: Scope, s2: Scope)(implicit delegates: Scope => Seq[Scope]): Option[Scope] = + private[sbt] def intersect(s1: Scope, s2: Scope)( + implicit delegates: Scope => Seq[Scope]): Option[Scope] = if (delegates(s1).contains(s2)) Some(s1) // s1 is more specific else if (delegates(s2).contains(s1)) Some(s2) // s2 is more specific else None - private[this] def deriveAndLocal(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope], scopeLocal: ScopeLocal): Seq[Setting[_]] = - { - import collection.mutable + private[this] def deriveAndLocal(init: Seq[Setting[_]])( + implicit delegates: Scope => Seq[Scope], + scopeLocal: ScopeLocal + ): Seq[Setting[_]] = { + import collection.mutable - final class Derived(val setting: DerivedSetting[_]) { - val dependencies = setting.dependencies.map(_.key) - def triggeredBy = dependencies.filter(setting.trigger) - val inScopes = new mutable.HashSet[Scope] - val outputs = new mutable.ListBuffer[Setting[_]] - } - final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) { - def dependencies = settings.flatMap(_.dependencies) - // This is mainly for use in the cyclic reference error message - override def toString = s"Derived settings for ${key.label}, ${definedAtString(settings.map(_.setting))}" - } - - // separate `derived` settings from normal settings (`defs`) - val (derived, rawDefs) = Util.separate[Setting[_], Derived, Setting[_]](init) { case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s) } - val defs = addLocal(rawDefs)(scopeLocal) - - // group derived settings by the key they define - val derivsByDef = new mutable.HashMap[AttributeKey[_], Deriveds] - for (s <- derived) { - val key = s.setting.key.key - derivsByDef.getOrElseUpdate(key, new Deriveds(key, new mutable.ListBuffer)).settings += s - } - - // index derived settings by triggering key. This maps a key to the list of settings potentially derived from it. - val derivedBy = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived]] - for (s <- derived; d <- s.triggeredBy) - derivedBy.getOrElseUpdate(d, new mutable.ListBuffer) += s - - // Map a DerivedSetting[_] to the `Derived` struct wrapping it. Used to ultimately replace a DerivedSetting with - // the `Setting`s that were actually derived from it: `Derived.outputs` - val derivedToStruct: Map[DerivedSetting[_], Derived] = (derived map { s => s.setting -> s }).toMap - - // set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present - val defined = new mutable.HashSet[ScopedKey[_]] - def addDefs(ss: Seq[Setting[_]]): Unit = { for (s <- ss) defined += s.key } - addDefs(defs) - - // true iff the scoped key is in `defined`, taking delegation into account - def isDefined(key: AttributeKey[_], scope: Scope) = - delegates(scope).exists(s => defined.contains(ScopedKey(s, key))) - - // true iff all dependencies of derived setting `d` have a value (potentially via delegation) in `scope` - def allDepsDefined(d: Derived, scope: Scope, local: Set[AttributeKey[_]]): Boolean = - d.dependencies.forall(dep => local(dep) || isDefined(dep, scope)) - - // Returns the list of injectable derived settings and their local settings for `sk`. - // The settings are to be injected under `outputScope` = whichever scope is more specific of: - // * the dependency's (`sk`) scope - // * the DerivedSetting's scope in which it has been declared, `definingScope` - // provided that these two scopes intersect. - // A derived setting is injectable if: - // 1. it has not been previously injected into outputScope - // 2. it applies to outputScope (as determined by its `filter`) - // 3. all of its dependencies are defined for outputScope (allowing for delegation) - // This needs to handle local settings because a derived setting wouldn't be injected if it's local setting didn't exist yet. - val deriveFor = (sk: ScopedKey[_]) => { - val derivedForKey: List[Derived] = derivedBy.get(sk.key).toList.flatten - val scope = sk.scope - def localAndDerived(d: Derived): Seq[Setting[_]] = { - def definingScope = d.setting.key.scope - val outputScope = intersect(scope, definingScope) - outputScope collect { - case s if !d.inScopes.contains(s) && d.setting.filter(s) => - val local = d.dependencies.flatMap(dep => scopeLocal(ScopedKey(s, dep))) - if (allDepsDefined(d, s, local.map(_.key.key).toSet)) { - d.inScopes.add(s) - val out = local :+ d.setting.setScope(s) - d.outputs ++= out - out - } else - Nil - } getOrElse Nil - } - derivedForKey.flatMap(localAndDerived) - } - - val processed = new mutable.HashSet[ScopedKey[_]] - - // derives settings, transitively so that a derived setting can trigger another - def process(rem: List[Setting[_]]): Unit = rem match { - case s :: ss => - val sk = s.key - val ds = if (processed.add(sk)) deriveFor(sk) else Nil - addDefs(ds) - process(ds ::: ss) - case Nil => - } - process(defs.toList) - - // Take all the original defs and DerivedSettings along with locals, replace each DerivedSetting with the actual - // settings that were derived. - val allDefs = addLocal(init)(scopeLocal) - allDefs flatMap { case d: DerivedSetting[_] => (derivedToStruct get d map (_.outputs)).toStream.flatten; case s => Stream(s) } + final class Derived(val setting: DerivedSetting[_]) { + val dependencies = setting.dependencies.map(_.key) + def triggeredBy = dependencies.filter(setting.trigger) + val inScopes = new mutable.HashSet[Scope] + val outputs = new mutable.ListBuffer[Setting[_]] } + final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) { + def dependencies = settings.flatMap(_.dependencies) + // This is mainly for use in the cyclic reference error message + override def toString = + s"Derived settings for ${key.label}, ${definedAtString(settings.map(_.setting))}" + } + + // separate `derived` settings from normal settings (`defs`) + val (derived, rawDefs) = Util.separate[Setting[_], Derived, Setting[_]](init) { + case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s) + } + val defs = addLocal(rawDefs)(scopeLocal) + + // group derived settings by the key they define + val derivsByDef = new mutable.HashMap[AttributeKey[_], Deriveds] + for (s <- derived) { + val key = s.setting.key.key + derivsByDef.getOrElseUpdate(key, new Deriveds(key, new mutable.ListBuffer)).settings += s + } + + // index derived settings by triggering key. This maps a key to the list of settings potentially derived from it. + val derivedBy = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived]] + for (s <- derived; d <- s.triggeredBy) + derivedBy.getOrElseUpdate(d, new mutable.ListBuffer) += s + + // Map a DerivedSetting[_] to the `Derived` struct wrapping it. Used to ultimately replace a DerivedSetting with + // the `Setting`s that were actually derived from it: `Derived.outputs` + val derivedToStruct: Map[DerivedSetting[_], Derived] = (derived map { s => + s.setting -> s + }).toMap + + // set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present + val defined = new mutable.HashSet[ScopedKey[_]] + def addDefs(ss: Seq[Setting[_]]): Unit = { for (s <- ss) defined += s.key } + addDefs(defs) + + // true iff the scoped key is in `defined`, taking delegation into account + def isDefined(key: AttributeKey[_], scope: Scope) = + delegates(scope).exists(s => defined.contains(ScopedKey(s, key))) + + // true iff all dependencies of derived setting `d` have a value (potentially via delegation) in `scope` + def allDepsDefined(d: Derived, scope: Scope, local: Set[AttributeKey[_]]): Boolean = + d.dependencies.forall(dep => local(dep) || isDefined(dep, scope)) + + // Returns the list of injectable derived settings and their local settings for `sk`. + // The settings are to be injected under `outputScope` = whichever scope is more specific of: + // * the dependency's (`sk`) scope + // * the DerivedSetting's scope in which it has been declared, `definingScope` + // provided that these two scopes intersect. + // A derived setting is injectable if: + // 1. it has not been previously injected into outputScope + // 2. it applies to outputScope (as determined by its `filter`) + // 3. all of its dependencies are defined for outputScope (allowing for delegation) + // This needs to handle local settings because a derived setting wouldn't be injected if it's local setting didn't exist yet. + val deriveFor = (sk: ScopedKey[_]) => { + val derivedForKey: List[Derived] = derivedBy.get(sk.key).toList.flatten + val scope = sk.scope + def localAndDerived(d: Derived): Seq[Setting[_]] = { + def definingScope = d.setting.key.scope + val outputScope = intersect(scope, definingScope) + outputScope collect { + case s if !d.inScopes.contains(s) && d.setting.filter(s) => + val local = d.dependencies.flatMap(dep => scopeLocal(ScopedKey(s, dep))) + if (allDepsDefined(d, s, local.map(_.key.key).toSet)) { + d.inScopes.add(s) + val out = local :+ d.setting.setScope(s) + d.outputs ++= out + out + } else + Nil + } getOrElse Nil + } + derivedForKey.flatMap(localAndDerived) + } + + val processed = new mutable.HashSet[ScopedKey[_]] + + // derives settings, transitively so that a derived setting can trigger another + def process(rem: List[Setting[_]]): Unit = rem match { + case s :: ss => + val sk = s.key + val ds = if (processed.add(sk)) deriveFor(sk) else Nil + addDefs(ds) + process(ds ::: ss) + case Nil => + } + process(defs.toList) + + // Take all the original defs and DerivedSettings along with locals, replace each DerivedSetting with the actual + // settings that were derived. + val allDefs = addLocal(init)(scopeLocal) + allDefs flatMap { + case d: DerivedSetting[_] => (derivedToStruct get d map (_.outputs)).toStream.flatten; + case s => Stream(s) + } + } + sealed trait Initialize[T] { def dependencies: Seq[ScopedKey[_]] def apply[S](g: T => S): Initialize[S] private[sbt] def mapReferenced(g: MapScoped): Initialize[T] private[sbt] def mapConstant(g: MapConstant): Initialize[T] + private[sbt] def validateReferenced(g: ValidateRef): ValidatedInit[T] = - validateKeyReferenced(new ValidateKeyRef { def apply[B](key: ScopedKey[B], selfRefOk: Boolean) = g(key) }) + validateKeyReferenced(new ValidateKeyRef { + def apply[B](key: ScopedKey[B], selfRefOk: Boolean) = g(key) + }) private[sbt] def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] @@ -411,33 +549,50 @@ trait Init[Scope] { def zipWith[S, U](o: Initialize[S])(f: (T, S) => U): Initialize[U] = zipTupled(o)(f.tupled) private[this] def zipTupled[S, U](o: Initialize[S])(f: ((T, S)) => U): Initialize[U] = new Apply[({ type l[L[x]] = (L[T], L[S]) })#l, U](f, (this, o), AList.tuple2[T, S]) + /** A fold on the static attributes of this and nested Initializes. */ private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S } + object Initialize { implicit def joinInitialize[T](s: Seq[Initialize[T]]): JoinInitSeq[T] = new JoinInitSeq(s) + final class JoinInitSeq[T](s: Seq[Initialize[T]]) { def joinWith[S](f: Seq[T] => S): Initialize[S] = uniform(s)(f) def join: Initialize[Seq[T]] = uniform(s)(idFun) } + def join[T](inits: Seq[Initialize[T]]): Initialize[Seq[T]] = uniform(inits)(idFun) + def joinAny[M[_]](inits: Seq[Initialize[M[T]] forSome { type T }]): Initialize[Seq[M[_]]] = - join(inits.asInstanceOf[Seq[Initialize[M[Any]]]]).asInstanceOf[Initialize[Seq[M[T] forSome { type T }]]] + join(inits.asInstanceOf[Seq[Initialize[M[Any]]]]) + .asInstanceOf[Initialize[Seq[M[T] forSome { type T }]]] } + object SettingsDefinition { implicit def unwrapSettingsDefinition(d: SettingsDefinition): Seq[Setting[_]] = d.settings - implicit def wrapSettingsDefinition(ss: Seq[Setting[_]]): SettingsDefinition = new SettingList(ss) + implicit def wrapSettingsDefinition(ss: Seq[Setting[_]]): SettingsDefinition = + new SettingList(ss) } + sealed trait SettingsDefinition { def settings: Seq[Setting[_]] } + final class SettingList(val settings: Seq[Setting[_]]) extends SettingsDefinition - sealed class Setting[T] private[Init] (val key: ScopedKey[T], val init: Initialize[T], val pos: SourcePosition) extends SettingsDefinition { + + sealed class Setting[T] private[Init] ( + val key: ScopedKey[T], + val init: Initialize[T], + val pos: SourcePosition + ) extends SettingsDefinition { def settings = this :: Nil def definitive: Boolean = !init.dependencies.contains(key) def dependencies: Seq[ScopedKey[_]] = remove(init.dependencies, key) def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos) - def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] = (init validateReferenced g).right.map(newI => make(key, newI, pos)) + + def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] = + (init validateReferenced g).right.map(newI => make(key, newI, pos)) private[sbt] def validateKeyReferenced(g: ValidateKeyRef): Either[Seq[Undefined], Setting[T]] = (init validateKeyReferenced g).right.map(newI => make(key, newI, pos)) @@ -446,38 +601,72 @@ trait Init[Scope] { def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = make(key, init(t => f(key, t)), pos) def mapConstant(g: MapConstant): Setting[T] = make(key, init mapConstant g, pos) def withPos(pos: SourcePosition) = make(key, init, pos) + def positionString: Option[String] = pos match { case pos: FilePosition => Some(pos.path + ":" + pos.startLine) case NoPosition => None } - private[sbt] def mapInitialize(f: Initialize[T] => Initialize[T]): Setting[T] = make(key, f(init), pos) + + private[sbt] def mapInitialize(f: Initialize[T] => Initialize[T]): Setting[T] = + make(key, f(init), pos) + override def toString = "setting(" + key + ") at " + pos - protected[this] def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = new Setting[B](key, init, pos) + protected[this] def make[B]( + key: ScopedKey[B], + init: Initialize[B], + pos: SourcePosition + ): Setting[B] = new Setting[B](key, init, pos) + protected[sbt] def isDerived: Boolean = false - private[sbt] def setScope(s: Scope): Setting[T] = make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos) + private[sbt] def setScope(s: Scope): Setting[T] = + make(key.copy(scope = s), init.mapReferenced(mapScope(const(s))), pos) + /** Turn this setting into a `DefaultSetting` if it's not already, otherwise returns `this` */ - private[sbt] def default(id: => Long = nextDefaultID()): DefaultSetting[T] = DefaultSetting(key, init, pos, id) + private[sbt] def default(id: => Long = nextDefaultID()): DefaultSetting[T] = + DefaultSetting(key, init, pos, id) } - private[Init] sealed class DerivedSetting[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, val filter: Scope => Boolean, val trigger: AttributeKey[_] => Boolean) extends Setting[T](sk, i, p) { - override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = new DerivedSetting[B](key, init, pos, filter, trigger) + + private[Init] sealed class DerivedSetting[T]( + sk: ScopedKey[T], + i: Initialize[T], + p: SourcePosition, + val filter: Scope => Boolean, + val trigger: AttributeKey[_] => Boolean + ) extends Setting[T](sk, i, p) { + + override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = + new DerivedSetting[B](key, init, pos, filter, trigger) + protected[sbt] override def isDerived: Boolean = true - override def default(_id: => Long): DefaultSetting[T] = new DerivedSetting[T](sk, i, p, filter, trigger) with DefaultSetting[T] { val id = _id } + + override def default(_id: => Long): DefaultSetting[T] = + new DerivedSetting[T](sk, i, p, filter, trigger) with DefaultSetting[T] { val id = _id } + override def toString = "derived " + super.toString } + // Only keep the first occurrence of this setting and move it to the front so that it has lower precedence than non-defaults. // This is intended for internal sbt use only, where alternatives like Plugin.globalSettings are not available. private[Init] sealed trait DefaultSetting[T] extends Setting[T] { val id: Long - override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = super.make(key, init, pos) default id + + override def make[B](key: ScopedKey[B], init: Initialize[B], pos: SourcePosition): Setting[B] = + super.make(key, init, pos) default id + override final def hashCode = id.hashCode - override final def equals(o: Any): Boolean = o match { case d: DefaultSetting[_] => d.id == id; case _ => false } + + override final def equals(o: Any): Boolean = o match { + case d: DefaultSetting[_] => d.id == id; case _ => false + } + override def toString = s"default($id) " + super.toString override def default(id: => Long) = this } object DefaultSetting { - def apply[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, _id: Long) = new Setting[T](sk, i, p) with DefaultSetting[T] { val id = _id } + def apply[T](sk: ScopedKey[T], i: Initialize[T], p: SourcePosition, _id: Long) = + new Setting[T](sk, i, p) with DefaultSetting[T] { val id = _id } } private[this] def handleUndefined[T](vr: ValidatedInit[T]): Initialize[T] = vr match { @@ -490,7 +679,9 @@ trait Init[Scope] { // mainly for reducing generated class count private[this] def validateKeyReferencedT(g: ValidateKeyRef) = - new (Initialize ~> ValidatedInit) { def apply[T](i: Initialize[T]) = i validateKeyReferenced g } + new (Initialize ~> ValidatedInit) { + def apply[T](i: Initialize[T]) = i validateKeyReferenced g + } private[this] def mapReferencedT(g: MapScoped) = new (Initialize ~> Initialize) { def apply[T](i: Initialize[T]) = i mapReferenced g } @@ -506,40 +697,55 @@ trait Init[Scope] { sealed trait Keyed[S, T] extends Initialize[T] { def scopedKey: ScopedKey[S] def transform: S => T + final def dependencies = scopedKey :: Nil final def apply[Z](g: T => Z): Initialize[Z] = new GetValue(scopedKey, g compose transform) final def evaluate(ss: Settings[Scope]): T = transform(getValue(ss, scopedKey)) final def mapReferenced(g: MapScoped): Initialize[T] = new GetValue(g(scopedKey), transform) - private[sbt] final def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] = g(scopedKey, false) match { - case Left(un) => Left(un :: Nil) - case Right(nk) => Right(new GetValue(nk, transform)) - } + + private[sbt] final def validateKeyReferenced(g: ValidateKeyRef): ValidatedInit[T] = + g(scopedKey, false) match { + case Left(un) => Left(un :: Nil) + case Right(nk) => Right(new GetValue(nk, transform)) + } + final def mapConstant(g: MapConstant): Initialize[T] = g(scopedKey) match { case None => this case Some(const) => new Value(() => transform(const)) } + private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = init } - private[this] final class GetValue[S, T](val scopedKey: ScopedKey[S], val transform: S => T) extends Keyed[S, T] + + private[this] final class GetValue[S, T](val scopedKey: ScopedKey[S], val transform: S => T) + extends Keyed[S, T] + trait KeyedInitialize[T] extends Keyed[T, T] { final val transform = idFun[T] } - private[sbt] final class TransformCapture(val f: Initialize ~> Initialize) extends Initialize[Initialize ~> Initialize] { + private[sbt] final class TransformCapture(val f: Initialize ~> Initialize) + extends Initialize[Initialize ~> Initialize] { def dependencies = Nil def apply[Z](g2: (Initialize ~> Initialize) => Z): Initialize[Z] = map(this)(g2) def evaluate(ss: Settings[Scope]): Initialize ~> Initialize = f def mapReferenced(g: MapScoped) = new TransformCapture(mapReferencedT(g) ∙ f) def mapConstant(g: MapConstant) = new TransformCapture(mapConstantT(g) ∙ f) - def validateKeyReferenced(g: ValidateKeyRef) = Right(new TransformCapture(getValidated ∙ validateKeyReferencedT(g) ∙ f)) + + def validateKeyReferenced(g: ValidateKeyRef) = + Right(new TransformCapture(getValidated ∙ validateKeyReferencedT(g) ∙ f)) + private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init } - private[sbt] final class ValidationCapture[T](val key: ScopedKey[T], val selfRefOk: Boolean) extends Initialize[ScopedKey[T]] { + + private[sbt] final class ValidationCapture[T](val key: ScopedKey[T], val selfRefOk: Boolean) + extends Initialize[ScopedKey[T]] { def dependencies = Nil def apply[Z](g2: ScopedKey[T] => Z): Initialize[Z] = map(this)(g2) def evaluate(ss: Settings[Scope]) = key def mapReferenced(g: MapScoped) = new ValidationCapture(g(key), selfRefOk) def mapConstant(g: MapConstant) = this + def validateKeyReferenced(g: ValidateKeyRef) = g(key, selfRefOk) match { case Left(un) => Left(un :: Nil) case Right(k) => Right(new ValidationCapture(k, selfRefOk)) @@ -547,34 +753,50 @@ trait Init[Scope] { private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init } - private[sbt] final class Bind[S, T](val f: S => Initialize[T], val in: Initialize[S]) extends Initialize[T] { + + private[sbt] final class Bind[S, T](val f: S => Initialize[T], val in: Initialize[S]) + extends Initialize[T] { def dependencies = in.dependencies def apply[Z](g: T => Z): Initialize[Z] = new Bind[S, Z](s => f(s)(g), in) def evaluate(ss: Settings[Scope]): T = f(in evaluate ss) evaluate ss def mapReferenced(g: MapScoped) = new Bind[S, T](s => f(s) mapReferenced g, in mapReferenced g) - def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).right.map { validIn => - new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn) + + def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).right.map { + validIn => + new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn) } + def mapConstant(g: MapConstant) = new Bind[S, T](s => f(s) mapConstant g, in mapConstant g) - private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = in.processAttributes(init)(f) + + private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = + in.processAttributes(init)(f) } - private[sbt] final class Optional[S, T](val a: Option[Initialize[S]], val f: Option[S] => T) extends Initialize[T] { + + private[sbt] final class Optional[S, T](val a: Option[Initialize[S]], val f: Option[S] => T) + extends Initialize[T] { def dependencies = deps(a.toList) def apply[Z](g: T => Z): Initialize[Z] = new Optional[S, Z](a, g compose f) def mapReferenced(g: MapScoped) = new Optional(a map mapReferencedT(g).fn, f) + def validateKeyReferenced(g: ValidateKeyRef) = a match { case None => Right(this) case Some(i) => Right(new Optional(i.validateKeyReferenced(g).right.toOption, f)) } + def mapConstant(g: MapConstant): Initialize[T] = new Optional(a map mapConstantT(g).fn, f) def evaluate(ss: Settings[Scope]): T = f(a.flatMap(i => trapBadRef(evaluateT(ss)(i)))) + // proper solution is for evaluate to be deprecated or for external use only and a new internal method returning Either be used - private[this] def trapBadRef[A](run: => A): Option[A] = try Some(run) catch { case e: InvalidReference => None } + private[this] def trapBadRef[A](run: => A): Option[A] = + try Some(run) + catch { case e: InvalidReference => None } + private[sbt] def processAttributes[B](init: B)(f: (B, AttributeMap) => B): B = a match { case None => init case Some(i) => i.processAttributes(init)(f) } } + private[sbt] final class Value[T](val value: () => T) extends Initialize[T] { def dependencies = Nil def mapReferenced(g: MapScoped) = this @@ -584,6 +806,7 @@ trait Init[Scope] { def evaluate(map: Settings[Scope]): T = value() private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init } + private[sbt] final object StaticScopes extends Initialize[Set[Scope]] { def dependencies = Nil def mapReferenced(g: MapScoped) = this @@ -593,23 +816,35 @@ trait Init[Scope] { def evaluate(map: Settings[Scope]) = map.scopes private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = init } - private[sbt] final class Apply[K[L[x]], T](val f: K[Id] => T, val inputs: K[Initialize], val alist: AList[K]) extends Initialize[T] { + + private[sbt] final class Apply[K[L[x]], T]( + val f: K[Id] => T, + val inputs: K[Initialize], + val alist: AList[K] + ) extends Initialize[T] { def dependencies = deps(alist.toList(inputs)) def mapReferenced(g: MapScoped) = mapInputs(mapReferencedT(g)) def apply[S](g: T => S) = new Apply(g compose f, inputs, alist) def mapConstant(g: MapConstant) = mapInputs(mapConstantT(g)) - def mapInputs(g: Initialize ~> Initialize): Initialize[T] = new Apply(f, alist.transform(inputs, g), alist) + + def mapInputs(g: Initialize ~> Initialize): Initialize[T] = + new Apply(f, alist.transform(inputs, g), alist) + def evaluate(ss: Settings[Scope]) = f(alist.transform(inputs, evaluateT(ss))) - def validateKeyReferenced(g: ValidateKeyRef) = - { - val tx = alist.transform(inputs, validateKeyReferencedT(g)) - val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten) - val get = new (ValidatedInit ~> Initialize) { def apply[B](vr: ValidatedInit[B]) = vr.right.get } - if (undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs) + + def validateKeyReferenced(g: ValidateKeyRef) = { + val tx = alist.transform(inputs, validateKeyReferencedT(g)) + val undefs = alist.toList(tx).flatMap(_.left.toSeq.flatten) + val get = new (ValidatedInit ~> Initialize) { + def apply[B](vr: ValidatedInit[B]) = vr.right.get } + if (undefs.isEmpty) Right(new Apply(f, alist.transform(tx, get), alist)) else Left(undefs) + } private[sbt] def processAttributes[S](init: S)(f: (S, AttributeMap) => S): S = - (init /: alist.toList(inputs)) { (v, i) => i.processAttributes(v)(f) } + (init /: alist.toList(inputs)) { (v, i) => + i.processAttributes(v)(f) + } } private def remove[T](s: Seq[T], v: T) = s filterNot (_ == v) } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala index 0c9fac038..bb339865b 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Signal.scala @@ -3,24 +3,25 @@ package sbt.internal.util object Signals { val CONT = "CONT" val INT = "INT" - def withHandler[T](handler: () => Unit, signal: String = INT)(action: () => T): T = - { - val result = - try { - val signals = new Signals0 - signals.withHandler(signal, handler, action) - } catch { case e: LinkageError => Right(action()) } - result match { - case Left(e) => throw e - case Right(v) => v - } + def withHandler[T](handler: () => Unit, signal: String = INT)(action: () => T): T = { + val result = + try { + val signals = new Signals0 + signals.withHandler(signal, handler, action) + } catch { case e: LinkageError => Right(action()) } + + result match { + case Left(e) => throw e + case Right(v) => v } + } /** Helper interface so we can expose internals of signal-isms to others. */ sealed trait Registration { def remove(): Unit } + /** * Register a signal handler that can be removed later. * NOTE: Does not stack with other signal handlers!!!! @@ -58,29 +59,25 @@ object Signals { // Must only be referenced using a // try { } catch { case e: LinkageError => ... } -// block to +// block to private final class Signals0 { - def supported(signal: String): Boolean = - { - import sun.misc.Signal - try { new Signal(signal); true } - catch { case e: IllegalArgumentException => false } - } + def supported(signal: String): Boolean = { + import sun.misc.Signal + try { new Signal(signal); true } catch { case e: IllegalArgumentException => false } + } // returns a LinkageError in `action` as Left(t) in order to avoid it being // incorrectly swallowed as missing Signal/SignalHandler - def withHandler[T](signal: String, handler: () => Unit, action: () => T): Either[Throwable, T] = - { - import sun.misc.{ Signal, SignalHandler } - val intSignal = new Signal(signal) - val newHandler = new SignalHandler { - def handle(sig: Signal): Unit = { handler() } - } - - val oldHandler = Signal.handle(intSignal, newHandler) - - try Right(action()) - catch { case e: LinkageError => Left(e) } - finally { Signal.handle(intSignal, oldHandler); () } + def withHandler[T](signal: String, handler: () => Unit, action: () => T): Either[Throwable, T] = { + import sun.misc.{ Signal, SignalHandler } + val intSignal = new Signal(signal) + val newHandler = new SignalHandler { + def handle(sig: Signal): Unit = { handler() } } + + val oldHandler = Signal.handle(intSignal, newHandler) + + try Right(action()) + catch { case e: LinkageError => Left(e) } finally { Signal.handle(intSignal, oldHandler); () } + } } diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala b/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala index b7aac6360..63c822103 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/TypeFunctions.scala @@ -20,9 +20,10 @@ trait TypeFunctions { def nestCon[M[_], N[_], G[_]](f: M ~> N): (M ∙ G)#l ~> (N ∙ G)#l = f.asInstanceOf[(M ∙ G)#l ~> (N ∙ G)#l] // implemented with a cast to avoid extra object+method call. castless version: + /* new ( (M ∙ G)#l ~> (N ∙ G)#l ) { - def apply[T](mg: M[G[T]]): N[G[T]] = f(mg) - }*/ + def apply[T](mg: M[G[T]]): N[G[T]] = f(mg) + } */ implicit def toFn1[A, B](f: A => B): Fn1[A, B] = new Fn1[A, B] { def ∙[C](g: C => A) = f compose g @@ -31,6 +32,7 @@ trait TypeFunctions { type Endo[T] = T => T type ~>|[A[_], B[_]] = A ~> Compose[Option, B]#Apply } + object TypeFunctions extends TypeFunctions trait ~>[-A[_], +B[_]] { outer => @@ -40,11 +42,13 @@ trait ~>[-A[_], +B[_]] { outer => final def ∙[C, D](g: C => D)(implicit ev: D <:< A[D]): C => B[D] = i => apply(ev(g(i))) final def fn[T] = (t: A[T]) => apply[T](t) } + object ~> { import TypeFunctions._ val Id: Id ~> Id = new (Id ~> Id) { def apply[T](a: T): T = a } implicit def tcIdEquals: (Id ~> Id) = Id } + trait Fn1[A, B] { def ∙[C](g: C => A): C => B -} \ No newline at end of file +} diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala index 75b8224c1..802457d11 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala @@ -11,11 +11,10 @@ object Util { def separateE[A, B](ps: Seq[Either[A, B]]): (Seq[A], Seq[B]) = separate(ps)(Types.idFun) - def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = - { - val (a, b) = ((Nil: Seq[A], Nil: Seq[B]) /: ps)((xs, y) => prependEither(xs, f(y))) - (a.reverse, b.reverse) - } + def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = { + val (a, b) = ((Nil: Seq[A], Nil: Seq[B]) /: ps)((xs, y) => prependEither(xs, f(y))) + (a.reverse, b.reverse) + } def prependEither[A, B](acc: (Seq[A], Seq[B]), next: Either[A, B]): (Seq[A], Seq[B]) = next match { diff --git a/internal/util-collection/src/test/scala/DagSpecification.scala b/internal/util-collection/src/test/scala/DagSpecification.scala index 3b3614e39..a52401fa6 100644 --- a/internal/util-collection/src/test/scala/DagSpecification.scala +++ b/internal/util-collection/src/test/scala/DagSpecification.scala @@ -9,44 +9,46 @@ import Prop._ import scala.collection.mutable.HashSet object DagSpecification extends Properties("Dag") { - property("No repeated nodes") = forAll { (dag: TestDag) => isSet(dag.topologicalSort) } - property("Sort contains node") = forAll { (dag: TestDag) => dag.topologicalSort.contains(dag) } - property("Dependencies precede node") = forAll { (dag: TestDag) => dependenciesPrecedeNodes(dag.topologicalSort) } + property("No repeated nodes") = forAll { (dag: TestDag) => + isSet(dag.topologicalSort) + } + property("Sort contains node") = forAll { (dag: TestDag) => + dag.topologicalSort.contains(dag) + } + property("Dependencies precede node") = forAll { (dag: TestDag) => + dependenciesPrecedeNodes(dag.topologicalSort) + } implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen)) - private def dagGen(nodeCount: Int): Gen[TestDag] = - { - val nodes = new HashSet[TestDag] - def nonterminalGen(p: Gen.Parameters): Gen[TestDag] = - { - val seed = rng.Seed.random() - for { - i <- 0 until nodeCount - nextDeps <- Gen.someOf(nodes).apply(p, seed) - } nodes += new TestDag(i, nextDeps) - for (nextDeps <- Gen.someOf(nodes)) yield new TestDag(nodeCount, nextDeps) - } - Gen.parameterized(nonterminalGen) + private def dagGen(nodeCount: Int): Gen[TestDag] = { + val nodes = new HashSet[TestDag] + def nonterminalGen(p: Gen.Parameters): Gen[TestDag] = { + val seed = rng.Seed.random() + for { + i <- 0 until nodeCount + nextDeps <- Gen.someOf(nodes).apply(p, seed) + } nodes += new TestDag(i, nextDeps) + for (nextDeps <- Gen.someOf(nodes)) yield new TestDag(nodeCount, nextDeps) } + Gen.parameterized(nonterminalGen) + } private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size - private def dependenciesPrecedeNodes(sort: List[TestDag]) = - { - val seen = new HashSet[TestDag] - def iterate(remaining: List[TestDag]): Boolean = - { - remaining match { - case Nil => true - case node :: tail => - if (node.dependencies.forall(seen.contains) && !seen.contains(node)) { - seen += node - iterate(tail) - } else - false - } - } - iterate(sort) + private def dependenciesPrecedeNodes(sort: List[TestDag]) = { + val seen = new HashSet[TestDag] + def iterate(remaining: List[TestDag]): Boolean = { + remaining match { + case Nil => true + case node :: tail => + if (node.dependencies.forall(seen.contains) && !seen.contains(node)) { + seen += node + iterate(tail) + } else + false + } } + iterate(sort) + } } class TestDag(id: Int, val dependencies: Iterable[TestDag]) extends Dag[TestDag] { override def toString = id + "->" + dependencies.mkString("[", ",", "]") diff --git a/internal/util-collection/src/test/scala/KeyTest.scala b/internal/util-collection/src/test/scala/KeyTest.scala index 461655b88..32479e6fd 100644 --- a/internal/util-collection/src/test/scala/KeyTest.scala +++ b/internal/util-collection/src/test/scala/KeyTest.scala @@ -6,13 +6,13 @@ import Prop._ object KeyTest extends Properties("AttributeKey") { property("equality") = { compare(AttributeKey[Int]("test"), AttributeKey[Int]("test"), true) && - compare(AttributeKey[Int]("test"), AttributeKey[Int]("test", "description"), true) && - compare(AttributeKey[Int]("test", "a"), AttributeKey[Int]("test", "b"), true) && - compare(AttributeKey[Int]("test"), AttributeKey[Int]("tests"), false) && - compare(AttributeKey[Int]("test"), AttributeKey[Double]("test"), false) && - compare(AttributeKey[java.lang.Integer]("test"), AttributeKey[Int]("test"), false) && - compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, String]]("test"), true) && - compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, _]]("test"), false) + compare(AttributeKey[Int]("test"), AttributeKey[Int]("test", "description"), true) && + compare(AttributeKey[Int]("test", "a"), AttributeKey[Int]("test", "b"), true) && + compare(AttributeKey[Int]("test"), AttributeKey[Int]("tests"), false) && + compare(AttributeKey[Int]("test"), AttributeKey[Double]("test"), false) && + compare(AttributeKey[java.lang.Integer]("test"), AttributeKey[Int]("test"), false) && + compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, String]]("test"), true) && + compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, _]]("test"), false) } def compare(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) = @@ -26,7 +26,7 @@ object KeyTest extends Properties("AttributeKey") { def compare0(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) = if (same) { ("equality" |: (a == b)) && - ("hash" |: (a.hashCode == b.hashCode)) + ("hash" |: (a.hashCode == b.hashCode)) } else ("equality" |: (a != b)) -} \ No newline at end of file +} diff --git a/internal/util-collection/src/test/scala/LiteralTest.scala b/internal/util-collection/src/test/scala/LiteralTest.scala index 9353a07bf..f38dc3d31 100644 --- a/internal/util-collection/src/test/scala/LiteralTest.scala +++ b/internal/util-collection/src/test/scala/LiteralTest.scala @@ -8,7 +8,9 @@ object LiteralTest { def x[A[_], B[_]](f: A ~> B) = f import Param._ - val f = x { (p: Param[Option, List]) => p.ret(p.in.toList) } + val f = x { (p: Param[Option, List]) => + p.ret(p.in.toList) + } val a: List[Int] = f(Some(3)) val b: List[String] = f(Some("aa")) diff --git a/internal/util-collection/src/test/scala/PMapTest.scala b/internal/util-collection/src/test/scala/PMapTest.scala index 9e1dde2c9..d81bf3a3c 100644 --- a/internal/util-collection/src/test/scala/PMapTest.scala +++ b/internal/util-collection/src/test/scala/PMapTest.scala @@ -15,4 +15,4 @@ object PMapTest { assert(y.head == 9) assert(y.tail.head == "a") assert(y.tail.tail == KNil) -} \ No newline at end of file +} diff --git a/internal/util-collection/src/test/scala/SettingsExample.scala b/internal/util-collection/src/test/scala/SettingsExample.scala index 20536a5ef..21b009cd1 100644 --- a/internal/util-collection/src/test/scala/SettingsExample.scala +++ b/internal/util-collection/src/test/scala/SettingsExample.scala @@ -3,7 +3,6 @@ package sbt.internal.util import sbt.util.Show /** Define our settings system */ - // A basic scope indexed by an integer. final case class Scope(nestIndex: Int, idAtIndex: Int = 0) @@ -14,15 +13,15 @@ final case class Scope(nestIndex: Int, idAtIndex: Int = 0) // That would be a general pain.) case class SettingsExample() extends Init[Scope] { // Provides a way of showing a Scope+AttributeKey[_] - val showFullKey: Show[ScopedKey[_]] = Show[ScopedKey[_]]((key: ScopedKey[_]) => - { - s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}" - }) + val showFullKey: Show[ScopedKey[_]] = Show[ScopedKey[_]]((key: ScopedKey[_]) => { + s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}" + }) // A sample delegation function that delegates to a Scope with a lower index. val delegates: Scope => Seq[Scope] = { case s @ Scope(index, proj) => - s +: (if (index <= 0) Nil else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index - 1)) }) + s +: (if (index <= 0) Nil + else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index - 1)) }) } // Not using this feature in this example. @@ -32,7 +31,6 @@ case class SettingsExample() extends Init[Scope] { } /** Usage Example **/ - case class SettingsUsage(val settingsExample: SettingsExample) { import settingsExample._ @@ -65,25 +63,25 @@ case class SettingsUsage(val settingsExample: SettingsExample) { }*/ /** - * Output: - * For the None results, we never defined the value and there was no value to delegate to. - * For a3, we explicitly defined it to be 3. - * a4 wasn't defined, so it delegates to a3 according to our delegates function. - * b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies by 3 - * a5 is defined as the previous value of a5 + 1 and - * since no previous value of a5 was defined, it delegates to a4, resulting in 3+1=4. - * b5 isn't defined explicitly, so it delegates to b4 and is therefore equal to 9 as well - * a0 = None - * b0 = None - * a1 = None - * b1 = None - * a2 = None - * b2 = None - * a3 = Some(3) - * b3 = None - * a4 = Some(3) - * b4 = Some(9) - * a5 = Some(4) - * b5 = Some(9) - */ + * Output: + * For the None results, we never defined the value and there was no value to delegate to. + * For a3, we explicitly defined it to be 3. + * a4 wasn't defined, so it delegates to a3 according to our delegates function. + * b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies by 3 + * a5 is defined as the previous value of a5 + 1 and + * since no previous value of a5 was defined, it delegates to a4, resulting in 3+1=4. + * b5 isn't defined explicitly, so it delegates to b4 and is therefore equal to 9 as well + * a0 = None + * b0 = None + * a1 = None + * b1 = None + * a2 = None + * b2 = None + * a3 = Some(3) + * b3 = None + * a4 = Some(3) + * b4 = Some(9) + * a5 = Some(4) + * b5 = Some(9) + */ } diff --git a/internal/util-collection/src/test/scala/SettingsTest.scala b/internal/util-collection/src/test/scala/SettingsTest.scala index 3e0bf0c0f..4dc8421d6 100644 --- a/internal/util-collection/src/test/scala/SettingsTest.scala +++ b/internal/util-collection/src/test/scala/SettingsTest.scala @@ -26,127 +26,131 @@ object SettingsTest extends Properties("settings") { } property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference } - final def allowedReference(intermediate: Int): Prop = - { - val top = value(intermediate) - def iterate(init: Initialize[Int]): Initialize[Int] = - bind(init) { t => - if (t <= 0) - top - else - iterate(value(t - 1)) - } - evaluate(setting(chk, iterate(top)) :: Nil); true - } - - property("Derived setting chain depending on (prev derived, normal setting)") = forAllNoShrink(Gen.choose(1, 100).label("numSettings")) { derivedSettings } - final def derivedSettings(nr: Int): Prop = - { - val genScopedKeys = { - // We wan - // t to generate lists of keys that DO NOT inclue the "ch" key we use to check things. - val attrKeys = mkAttrKeys[Int](nr).filter(_.forall(_.label != "ch")) - attrKeys map (_ map (ak => ScopedKey(Scope(0), ak))) - }.label("scopedKeys").filter(_.nonEmpty) - forAll(genScopedKeys) { scopedKeys => - try { - // Note; It's evil to grab last IF you haven't verified the set can't be empty. - val last = scopedKeys.last - val derivedSettings: Seq[Setting[Int]] = ( - for { - List(scoped0, scoped1) <- chk :: scopedKeys sliding 2 - nextInit = if (scoped0 == chk) chk - else (scoped0 zipWith chk) { (p, _) => p + 1 } - } yield derive(setting(scoped1, nextInit)) - ).toSeq - - { - // Note: This causes a cycle refernec error, quite frequently. - checkKey(last, Some(nr - 1), evaluate(setting(chk, value(0)) +: derivedSettings)) :| "Not derived?" - } && { - checkKey(last, None, evaluate(derivedSettings)) :| "Should not be derived" - } - } catch { - case t: Throwable => - // TODO - For debugging only. - t.printStackTrace(System.err) - throw t + final def allowedReference(intermediate: Int): Prop = { + val top = value(intermediate) + def iterate(init: Initialize[Int]): Initialize[Int] = + bind(init) { t => + if (t <= 0) + top + else + iterate(value(t - 1)) + } + evaluate(setting(chk, iterate(top)) :: Nil); true + } + + property("Derived setting chain depending on (prev derived, normal setting)") = + forAllNoShrink(Gen.choose(1, 100).label("numSettings")) { derivedSettings } + final def derivedSettings(nr: Int): Prop = { + val genScopedKeys = { + // We wan + // t to generate lists of keys that DO NOT inclue the "ch" key we use to check things. + val attrKeys = mkAttrKeys[Int](nr).filter(_.forall(_.label != "ch")) + attrKeys map (_ map (ak => ScopedKey(Scope(0), ak))) + }.label("scopedKeys").filter(_.nonEmpty) + forAll(genScopedKeys) { scopedKeys => + try { + // Note; It's evil to grab last IF you haven't verified the set can't be empty. + val last = scopedKeys.last + val derivedSettings: Seq[Setting[Int]] = ( + for { + List(scoped0, scoped1) <- chk :: scopedKeys sliding 2 + nextInit = if (scoped0 == chk) chk + else + (scoped0 zipWith chk) { (p, _) => + p + 1 + } + } yield derive(setting(scoped1, nextInit)) + ).toSeq + + { + // Note: This causes a cycle refernec error, quite frequently. + checkKey(last, Some(nr - 1), evaluate(setting(chk, value(0)) +: derivedSettings)) :| "Not derived?" + } && { + checkKey(last, None, evaluate(derivedSettings)) :| "Should not be derived" } + } catch { + case t: Throwable => + // TODO - For debugging only. + t.printStackTrace(System.err) + throw t } } + } - private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] = - { - import Gen._ - val nonEmptyAlphaStr = - nonEmptyListOf(alphaChar).map(_.mkString).suchThat(_.forall(_.isLetter)) + private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] = { + import Gen._ + val nonEmptyAlphaStr = + nonEmptyListOf(alphaChar).map(_.mkString).suchThat(_.forall(_.isLetter)) - (for { - list <- Gen.listOfN(nr, nonEmptyAlphaStr) suchThat (l => l.size == l.distinct.size) - item <- list - } yield AttributeKey[T](item)).label(s"mkAttrKeys($nr)") + (for { + list <- Gen.listOfN(nr, nonEmptyAlphaStr) suchThat (l => l.size == l.distinct.size) + item <- list + } yield AttributeKey[T](item)).label(s"mkAttrKeys($nr)") + } + + property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = + derivedKeepsPosition + final def derivedKeepsPosition: Prop = { + val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a")) + val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b")) + val prop1 = { + val settings: Seq[Setting[_]] = Seq( + setting(a, value(3)), + setting(b, value(6)), + derive(setting(b, a)), + setting(a, value(5)), + setting(b, value(8)) + ) + val ev = evaluate(settings) + checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev) } + val prop2 = { + val settings: Seq[Setting[Int]] = Seq( + setting(a, value(3)), + setting(b, value(6)), + derive(setting(b, a)), + setting(a, value(5)) + ) + val ev = evaluate(settings) + checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev) + } + prop1 && prop2 + } - property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = derivedKeepsPosition - final def derivedKeepsPosition: Prop = - { - val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a")) - val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b")) - val prop1 = { - val settings: Seq[Setting[_]] = Seq( - setting(a, value(3)), - setting(b, value(6)), - derive(setting(b, a)), - setting(a, value(5)), - setting(b, value(8)) - ) + property( + "DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=" + ) = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope } + final def derivedSettingsScope(nrProjects: Int): Prop = { + forAll(mkAttrKeys[Int](2)) { + case List(key, derivedKey) => + val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key) + val projectDerivedKeys = for { proj <- 1 to nrProjects } yield + ScopedKey(Scope(1, proj), derivedKey) + val globalKey = ScopedKey(Scope(0), key) + val globalDerivedKey = ScopedKey(Scope(0), derivedKey) + // Each project defines an initial value, but the update is defined in globalKey. + // However, the derived Settings that come from this should be scoped in each project. + val settings: Seq[Setting[_]] = + derive(setting(globalDerivedKey, settingsExample.map(globalKey)(_ + 1))) +: projectKeys + .map(pk => setting(pk, value(0))) val ev = evaluate(settings) - checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev) - } - val prop2 = { - val settings: Seq[Setting[Int]] = Seq( - setting(a, value(3)), - setting(b, value(6)), - derive(setting(b, a)), - setting(a, value(5)) - ) - val ev = evaluate(settings) - checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev) - } - prop1 && prop2 - } - - property("DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope } - final def derivedSettingsScope(nrProjects: Int): Prop = - { - forAll(mkAttrKeys[Int](2)) { - case List(key, derivedKey) => - val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key) - val projectDerivedKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), derivedKey) - val globalKey = ScopedKey(Scope(0), key) - val globalDerivedKey = ScopedKey(Scope(0), derivedKey) - // Each project defines an initial value, but the update is defined in globalKey. - // However, the derived Settings that come from this should be scoped in each project. - val settings: Seq[Setting[_]] = - derive(setting(globalDerivedKey, settingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0))) - val ev = evaluate(settings) - // Also check that the key has no value at the "global" scope - val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev) - checkKey(globalDerivedKey, None, ev) && Prop.all(props: _*) - } + // Also check that the key has no value at the "global" scope + val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev) + checkKey(globalDerivedKey, None, ev) && Prop.all(props: _*) } + } // Circular (dynamic) references currently loop infinitely. // This is the expected behavior (detecting dynamic cycles is expensive), // but it may be necessary to provide an option to detect them (with a performance hit) // This would test that cycle detection. - // property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ } - final def checkCircularReferences(intermediate: Int): Prop = - { - val ccr = new CCR(intermediate) - try { evaluate(setting(chk, ccr.top) :: Nil); false } - catch { case e: java.lang.Exception => true } + // property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ } + final def checkCircularReferences(intermediate: Int): Prop = { + val ccr = new CCR(intermediate) + try { evaluate(setting(chk, ccr.top) :: Nil); false } catch { + case e: java.lang.Exception => true } + } def tests = for (i <- 0 to 5; k <- Seq(a, b)) yield { @@ -154,7 +158,8 @@ object SettingsTest extends Properties("settings") { checkKey[Int](ScopedKey(Scope(i), k), expected, applied) } - lazy val expectedValues = None :: None :: None :: None :: None :: None :: Some(3) :: None :: Some(3) :: Some(9) :: Some(4) :: Some(9) :: Nil + lazy val expectedValues = None :: None :: None :: None :: None :: None :: Some(3) :: None :: + Some(3) :: Some(9) :: Some(4) :: Some(9) :: Nil lazy val ch = AttributeKey[Int]("ch") lazy val chk = ScopedKey(Scope(0), ch) @@ -165,26 +170,25 @@ object SettingsTest extends Properties("settings") { bind(prev) { v => if (v <= 0) prev else chainBind(value(v - 1)) } - def singleIntTest(i: Initialize[Int], expected: Int) = - { - val eval = evaluate(setting(chk, i) :: Nil) - checkKey(chk, Some(expected), eval) - } + def singleIntTest(i: Initialize[Int], expected: Int) = { + val eval = evaluate(setting(chk, i) :: Nil) + checkKey(chk, Some(expected), eval) + } - def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) = - { - val value = settings.get(key.scope, key.key) - ("Key: " + key) |: - ("Value: " + value) |: - ("Expected: " + expected) |: - (value == expected) - } + def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) = { + val value = settings.get(key.scope, key.key) + ("Key: " + key) |: + ("Value: " + value) |: + ("Expected: " + expected) |: + (value == expected) + } def evaluate(settings: Seq[Setting[_]]): Settings[Scope] = - try { make(settings)(delegates, scopeLocal, showFullKey) } - catch { case e: Throwable => e.printStackTrace; throw e } + try { make(settings)(delegates, scopeLocal, showFullKey) } catch { + case e: Throwable => e.printStackTrace; throw e + } } -// This setup is a workaround for module synchronization issues +// This setup is a workaround for module synchronization issues final class CCR(intermediate: Int) { import SettingsTest.settingsExample._ lazy val top = iterate(value(intermediate), intermediate) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index b4d5d5f83..d9ad18859 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -15,7 +15,10 @@ abstract class JLine extends LineReader { protected[this] def reader: ConsoleReader protected[this] def injectThreadSleep: Boolean protected[this] val in: InputStream = JLine.makeInputStream(injectThreadSleep) - def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { unsynchronizedReadLine(prompt, mask) } + + def readLine(prompt: String, mask: Option[Char] = None) = JLine.withJLine { + unsynchronizedReadLine(prompt, mask) + } private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]): Option[String] = readLineWithHistory(prompt, mask) map { x => @@ -25,34 +28,35 @@ abstract class JLine extends LineReader { private[this] def readLineWithHistory(prompt: String, mask: Option[Char]): Option[String] = reader.getHistory match { case fh: FileHistory => - try { readLineDirect(prompt, mask) } - finally { fh.flush() } + try readLineDirect(prompt, mask) + finally fh.flush() case _ => readLineDirect(prompt, mask) } private[this] def readLineDirect(prompt: String, mask: Option[Char]): Option[String] = if (handleCONT) - Signals.withHandler(() => resume(), signal = Signals.CONT)(() => readLineDirectRaw(prompt, mask)) + Signals.withHandler(() => resume(), signal = Signals.CONT)(() => + readLineDirectRaw(prompt, mask)) else readLineDirectRaw(prompt, mask) - private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] = - { - val newprompt = handleMultilinePrompt(prompt) - try { - mask match { - case Some(m) => Option(reader.readLine(newprompt, m)) - case None => Option(reader.readLine(newprompt)) - } - } catch { - case e: InterruptedException => Option("") + + private[this] def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] = { + val newprompt = handleMultilinePrompt(prompt) + try { + mask match { + case Some(m) => Option(reader.readLine(newprompt, m)) + case None => Option(reader.readLine(newprompt)) } + } catch { + case e: InterruptedException => Option("") } + } private[this] def handleMultilinePrompt(prompt: String): String = { val lines = """\r?\n""".r.split(prompt) lines.length match { case 0 | 1 => prompt - case _ => + case _ => // Workaround for regression jline/jline2#205 reader.getOutput.write(lines.init.mkString("\n") + "\n") lines.last @@ -66,6 +70,7 @@ abstract class JLine extends LineReader { reader.flush() } } + private[sbt] object JLine { private[this] val TerminalProperty = "jline.terminal" @@ -75,18 +80,19 @@ private[sbt] object JLine { // older Scala, since it shaded classes but not the system property private[sbt] def fixTerminalProperty(): Unit = { val newValue = System.getProperty(TerminalProperty) match { - case "jline.UnixTerminal" => "unix" + case "jline.UnixTerminal" => "unix" case null if System.getProperty("sbt.cygwin") != null => "unix" - case "jline.WindowsTerminal" => "windows" - case "jline.AnsiWindowsTerminal" => "windows" - case "jline.UnsupportedTerminal" => "none" - case x => x + case "jline.WindowsTerminal" => "windows" + case "jline.AnsiWindowsTerminal" => "windows" + case "jline.UnsupportedTerminal" => "none" + case x => x } if (newValue != null) System.setProperty(TerminalProperty, newValue) () } protected[this] val originalIn = new FileInputStream(FileDescriptor.in) + private[sbt] def makeInputStream(injectThreadSleep: Boolean): InputStream = if (injectThreadSleep) new InputStreamWrapper(originalIn, Duration("50 ms")) else originalIn @@ -94,11 +100,13 @@ private[sbt] object JLine { // When calling this, ensure that enableEcho has been or will be called. // TerminalFactory.get will initialize the terminal to disable echo. private def terminal = jline.TerminalFactory.get + private def withTerminal[T](f: jline.Terminal => T): T = synchronized { val t = terminal t.synchronized { f(t) } } + /** * For accessing the JLine Terminal object. * This ensures synchronized access as well as re-enabling echo after getting the Terminal. @@ -108,7 +116,9 @@ private[sbt] object JLine { t.restore f(t) } + def createReader(): ConsoleReader = createReader(None, JLine.makeInputStream(true)) + def createReader(historyPath: Option[File], in: InputStream): ConsoleReader = usingTerminal { t => val cr = new ConsoleReader(in, System.out) @@ -122,41 +132,42 @@ private[sbt] object JLine { cr.setHistory(h) cr } + def withJLine[T](action: => T): T = withTerminal { t => t.init - try { action } - finally { t.restore } + try { action } finally { t.restore } } def simple( - historyPath: Option[File], - handleCONT: Boolean = HandleCONT, - injectThreadSleep: Boolean = false + historyPath: Option[File], + handleCONT: Boolean = HandleCONT, + injectThreadSleep: Boolean = false ): SimpleReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep) + val MaxHistorySize = 500 - val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT) + + val HandleCONT = + !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT) } -private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration) extends FilterInputStream(is) { - @tailrec - final override def read(): Int = +private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration) + extends FilterInputStream(is) { + @tailrec final override def read(): Int = if (is.available() != 0) is.read() else { Thread.sleep(poll.toMillis) read() } - @tailrec - final override def read(b: Array[Byte]): Int = + @tailrec final override def read(b: Array[Byte]): Int = if (is.available() != 0) is.read(b) else { Thread.sleep(poll.toMillis) read(b) } - @tailrec - final override def read(b: Array[Byte], off: Int, len: Int): Int = + @tailrec final override def read(b: Array[Byte], off: Int, len: Int): Int = if (is.available() != 0) is.read(b, off, len) else { Thread.sleep(poll.toMillis) @@ -167,23 +178,26 @@ private[sbt] class InputStreamWrapper(is: InputStream, val poll: Duration) exten trait LineReader { def readLine(prompt: String, mask: Option[Char] = None): Option[String] } + final class FullReader( - historyPath: Option[File], - complete: Parser[_], - val handleCONT: Boolean = JLine.HandleCONT, - val injectThreadSleep: Boolean = false + historyPath: Option[File], + complete: Parser[_], + val handleCONT: Boolean = JLine.HandleCONT, + val injectThreadSleep: Boolean = false ) extends JLine { - protected[this] val reader = - { - val cr = JLine.createReader(historyPath, in) - sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete) - cr - } + protected[this] val reader = { + val cr = JLine.createReader(historyPath, in) + sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete) + cr + } } -class SimpleReader private[sbt] (historyPath: Option[File], val handleCONT: Boolean, val injectThreadSleep: Boolean) extends JLine { +class SimpleReader private[sbt] ( + historyPath: Option[File], + val handleCONT: Boolean, + val injectThreadSleep: Boolean +) extends JLine { protected[this] val reader = JLine.createReader(historyPath, in) - } -object SimpleReader extends SimpleReader(None, JLine.HandleCONT, false) +object SimpleReader extends SimpleReader(None, JLine.HandleCONT, false) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala index 47dbb3b4f..64f6f1618 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala @@ -11,18 +11,28 @@ package complete */ sealed trait Completions { def get: Set[Completion] + final def x(o: Completions): Completions = flatMap(_ x o) final def ++(o: Completions): Completions = Completions(get ++ o.get) final def +:(o: Completion): Completions = Completions(get + o) final def filter(f: Completion => Boolean): Completions = Completions(get filter f) final def filterS(f: String => Boolean): Completions = filter(c => f(c.append)) + override def toString = get.mkString("Completions(", ",", ")") - final def flatMap(f: Completion => Completions): Completions = Completions(get.flatMap(c => f(c).get)) + + final def flatMap(f: Completion => Completions): Completions = + Completions(get.flatMap(c => f(c).get)) + final def map(f: Completion => Completion): Completions = Completions(get map f) + override final def hashCode = get.hashCode - override final def equals(o: Any) = o match { case c: Completions => get == c.get; case _ => false } + override final def equals(o: Any) = o match { + case c: Completions => get == c.get; case _ => false + } } + object Completions { + /** Returns a lazy Completions instance using the provided Completion Set. */ def apply(cs: => Set[Completion]): Completions = new Completions { lazy val get = cs @@ -45,6 +55,7 @@ object Completions { /** Returns a strict Completions instance containing only the provided Completion.*/ def single(c: Completion): Completions = strict(Set.empty + c) + } /** @@ -63,33 +74,45 @@ object Completions { * 2) the full token being completed, which is useful for presenting a user with choices to select */ sealed trait Completion { + /** The proposed suffix to append to the existing input to complete the last token in the input.*/ def append: String + /** The string to present to the user to represent the full token being suggested.*/ def display: String + /** True if this Completion is suggesting the empty string.*/ def isEmpty: Boolean /** Appends the completions in `o` with the completions in this Completion.*/ def ++(o: Completion): Completion = Completion.concat(this, o) - final def x(o: Completions): Completions = if (Completion evaluatesRight this) o.map(this ++ _) else Completions.strict(Set.empty + this) + + final def x(o: Completions): Completions = + if (Completion evaluatesRight this) o.map(this ++ _) else Completions.strict(Set.empty + this) + override final lazy val hashCode = Completion.hashCode(this) - override final def equals(o: Any) = o match { case c: Completion => Completion.equal(this, c); case _ => false } + override final def equals(o: Any) = o match { + case c: Completion => Completion.equal(this, c); case _ => false + } } + final class DisplayOnly(val display: String) extends Completion { def isEmpty = display.isEmpty def append = "" override def toString = "{" + display + "}" } + final class Token(val display: String, val append: String) extends Completion { def isEmpty = display.isEmpty && append.isEmpty override final def toString = "[" + display + "]++" + append } + final class Suggestion(val append: String) extends Completion { def isEmpty = append.isEmpty def display = append override def toString = append } + object Completion { def concat(a: Completion, b: Completion): Completion = (a, b) match { @@ -98,6 +121,7 @@ object Completion { case _ if a.isEmpty => b case _ => a } + def evaluatesRight(a: Completion): Boolean = a match { case _: Suggestion => true @@ -127,7 +151,8 @@ object Completion { def displayOnly(value: => String): Completion = new DisplayOnly(value) // TODO: make strict in 0.13.0 to match Token - def token(prepend: => String, append: => String): Completion = new Token(prepend + append, append) + def token(prepend: => String, append: => String): Completion = + new Token(prepend + append, append) /** @since 0.12.1 */ def tokenDisplay(append: String, display: String): Completion = new Token(display, append) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala index 79f488554..e9536f0e1 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala @@ -5,12 +5,23 @@ import java.lang.Character.{ toLowerCase => lower } /** @author Paul Phillips*/ object EditDistance { + /** * Translated from the java version at * http://www.merriampark.com/ld.htm * which is declared to be public domain. */ - def levenshtein(s: String, t: String, insertCost: Int = 1, deleteCost: Int = 1, subCost: Int = 1, transposeCost: Int = 1, matchCost: Int = 0, caseCost: Int = 1, transpositions: Boolean = false): Int = { + def levenshtein( + s: String, + t: String, + insertCost: Int = 1, + deleteCost: Int = 1, + subCost: Int = 1, + transposeCost: Int = 1, + matchCost: Int = 0, + caseCost: Int = 1, + transpositions: Boolean = false + ): Int = { val n = s.length val m = t.length if (n == 0) return m diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala index 6539554a6..54ac83a4e 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala @@ -10,6 +10,7 @@ import sbt.io.IO * TAB key in the console. */ trait ExampleSource { + /** * @return a (possibly lazy) list of completion example strings. These strings are continuations of user's input. The * user's input is incremented with calls to [[withAddedPrefix]]. @@ -22,6 +23,7 @@ trait ExampleSource { * the just added prefix). */ def withAddedPrefix(addedPrefix: String): ExampleSource + } /** @@ -29,7 +31,8 @@ trait ExampleSource { * @param examples the examples that will be displayed to the user when they press the TAB key. */ sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource { - override def withAddedPrefix(addedPrefix: String): ExampleSource = FixedSetExamples(examplesWithRemovedPrefix(addedPrefix)) + override def withAddedPrefix(addedPrefix: String): ExampleSource = + FixedSetExamples(examplesWithRemovedPrefix(addedPrefix)) override def apply(): Iterable[String] = examples @@ -46,12 +49,17 @@ sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSo class FileExamples(base: File, prefix: String = "") extends ExampleSource { override def apply(): Stream[String] = files(base).map(_ substring prefix.length) - override def withAddedPrefix(addedPrefix: String): FileExamples = new FileExamples(base, prefix + addedPrefix) + override def withAddedPrefix(addedPrefix: String): FileExamples = + new FileExamples(base, prefix + addedPrefix) protected def files(directory: File): Stream[String] = { val childPaths = IO.listFiles(directory).toStream - val prefixedDirectChildPaths = childPaths map { IO.relativize(base, _).get } filter { _ startsWith prefix } - val dirsToRecurseInto = childPaths filter { _.isDirectory } map { IO.relativize(base, _).get } filter { dirStartsWithPrefix } + val prefixedDirectChildPaths = childPaths map { IO.relativize(base, _).get } filter { + _ startsWith prefix + } + val dirsToRecurseInto = childPaths filter { _.isDirectory } map { IO.relativize(base, _).get } filter { + dirStartsWithPrefix + } prefixedDirectChildPaths append dirsToRecurseInto.flatMap(dir => files(new File(base, dir))) } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala index d5a96836e..23d60d411 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala @@ -7,13 +7,20 @@ package complete import History.number import java.io.File -final class History private (val lines: IndexedSeq[String], val path: Option[File], error: String => Unit) { +final class History private ( + val lines: IndexedSeq[String], + val path: Option[File], + error: String => Unit +) { private def reversed = lines.reverse def all: Seq[String] = lines def size = lines.length def !! : Option[String] = !-(1) - def apply(i: Int): Option[String] = if (0 <= i && i < size) Some(lines(i)) else { sys.error("Invalid history index: " + i) } + + def apply(i: Int): Option[String] = + if (0 <= i && i < size) Some(lines(i)) else { sys.error("Invalid history index: " + i) } + def !(i: Int): Option[String] = apply(i) def !(s: String): Option[String] = @@ -21,6 +28,7 @@ final class History private (val lines: IndexedSeq[String], val path: Option[Fil case Some(n) => if (n < 0) !-(-n) else apply(n) case None => nonEmpty(s) { reversed.find(_.startsWith(s)) } } + def !-(n: Int): Option[String] = apply(size - n - 1) def !?(s: String): Option[String] = nonEmpty(s) { reversed.drop(1).find(_.contains(s)) } @@ -32,13 +40,17 @@ final class History private (val lines: IndexedSeq[String], val path: Option[Fil act def list(historySize: Int, show: Int): Seq[String] = - lines.toList.drop(scala.math.max(0, lines.size - historySize)).zipWithIndex.map { case (line, number) => " " + number + " " + line }.takeRight(show max 1) + lines.toList + .drop(scala.math.max(0, lines.size - historySize)) + .zipWithIndex + .map { case (line, number) => " " + number + " " + line } + .takeRight(show max 1) } object History { - def apply(lines: Seq[String], path: Option[File], error: String => Unit): History = new History(lines.toIndexedSeq, path, sys.error) + def apply(lines: Seq[String], path: Option[File], error: String => Unit): History = + new History(lines.toIndexedSeq, path, sys.error) def number(s: String): Option[Int] = - try { Some(s.toInt) } - catch { case e: NumberFormatException => None } + try { Some(s.toInt) } catch { case e: NumberFormatException => None } } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala index f18f1619f..bf34ea047 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala @@ -35,9 +35,14 @@ object HistoryCommands { StartsWithString -> "Execute the most recent command starting with 'string'", ContainsString -> "Execute the most recent command containing 'string'" ) - def helpString = "History commands:\n " + (descriptions.map { case (c, d) => c + " " + d }).mkString("\n ") - def printHelp(): Unit = - println(helpString) + + def helpString = + "History commands:\n " + (descriptions + .map { case (c, d) => c + " " + d }) + .mkString("\n ") + + def printHelp(): Unit = println(helpString) + def printHistory(history: complete.History, historySize: Int, show: Int): Unit = history.list(historySize, show).foreach(println) @@ -46,26 +51,32 @@ object HistoryCommands { val MaxLines = 500 lazy val num = token(NatBasic, "") lazy val last = Last ^^^ { execute(_.!!) } - lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => (h: History) => { printHistory(h, MaxLines, show); Some(Nil) } + + lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => (h: History) => + { printHistory(h, MaxLines, show); Some(Nil) } } + lazy val execStr = flag('?') ~ token(any.+.string, "") map { case (contains, str) => execute(h => if (contains) h !? str else h ! str) } + lazy val execInt = flag('-') ~ num map { case (neg, value) => execute(h => if (neg) h !- value else h ! value) } + lazy val help = success((h: History) => { printHelp(); Some(Nil) }) - def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => - { - val command = f(h).filterNot(_.startsWith(Start)) - val lines = h.lines.toArray - command.foreach(lines(lines.length - 1) = _) - h.path foreach { h => IO.writeLines(h, lines) } - Some(command.toList) + def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => { + val command = f(h).filterNot(_.startsWith(Start)) + val lines = h.lines.toArray + command.foreach(lines(lines.length - 1) = _) + h.path foreach { h => + IO.writeLines(h, lines) } + Some(command.toList) + } val actionParser: Parser[complete.History => Option[List[String]]] = Start ~> (help | last | execInt | list | execStr) // execStr must come last diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala index 0b8b50502..b779ace36 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala @@ -12,19 +12,31 @@ import collection.JavaConversions object JLineCompletion { def installCustomCompletor(reader: ConsoleReader, parser: Parser[_]): Unit = installCustomCompletor(reader)(parserAsCompletor(parser)) - def installCustomCompletor(reader: ConsoleReader)(complete: (String, Int) => (Seq[String], Seq[String])): Unit = - installCustomCompletor(customCompletor(complete), reader) - def installCustomCompletor(complete: (ConsoleReader, Int) => Boolean, reader: ConsoleReader): Unit = - { - reader.removeCompleter(DummyCompletor) - reader.addCompleter(DummyCompletor) - reader.setCompletionHandler(new CustomHandler(complete)) - } - private[this] final class CustomHandler(completeImpl: (ConsoleReader, Int) => Boolean) extends CompletionHandler { + def installCustomCompletor(reader: ConsoleReader)( + complete: (String, Int) => (Seq[String], Seq[String]) + ): Unit = + installCustomCompletor(customCompletor(complete), reader) + + def installCustomCompletor( + complete: (ConsoleReader, Int) => Boolean, + reader: ConsoleReader + ): Unit = { + reader.removeCompleter(DummyCompletor) + reader.addCompleter(DummyCompletor) + reader.setCompletionHandler(new CustomHandler(complete)) + } + + private[this] final class CustomHandler(completeImpl: (ConsoleReader, Int) => Boolean) + extends CompletionHandler { private[this] var previous: Option[(String, Int)] = None private[this] var level: Int = 1 - override def complete(reader: ConsoleReader, candidates: java.util.List[CharSequence], position: Int) = { + + override def complete( + reader: ConsoleReader, + candidates: java.util.List[CharSequence], + position: Int + ) = { val current = Some(bufferSnapshot(reader)) level = if (current == previous) level + 1 else 1 previous = current @@ -42,68 +54,74 @@ object JLineCompletion { // (ConsoleReader doesn't call the handler if there aren't any completions) // the custom handler will then throw away the candidates and call the custom function private[this] final object DummyCompletor extends Completer { - override def complete(buffer: String, cursor: Int, candidates: java.util.List[CharSequence]): Int = - { - candidates.asInstanceOf[java.util.List[String]] add "dummy" - 0 - } + override def complete( + buffer: String, + cursor: Int, + candidates: java.util.List[CharSequence] + ): Int = { + candidates.asInstanceOf[java.util.List[String]] add "dummy" + 0 + } } def parserAsCompletor(p: Parser[_]): (String, Int) => (Seq[String], Seq[String]) = (str, level) => convertCompletions(Parser.completions(p, str, level)) - def convertCompletions(c: Completions): (Seq[String], Seq[String]) = - { - val cs = c.get - if (cs.isEmpty) - (Nil, "{invalid input}" :: Nil) - else - convertCompletions(cs) - } - def convertCompletions(cs: Set[Completion]): (Seq[String], Seq[String]) = - { - val (insert, display) = - ((Set.empty[String], Set.empty[String]) /: cs) { - case (t @ (insert, display), comp) => - if (comp.isEmpty) t else (insert + comp.append, appendNonEmpty(display, comp.display)) - } - (insert.toSeq, display.toSeq.sorted) - } + def convertCompletions(c: Completions): (Seq[String], Seq[String]) = { + val cs = c.get + if (cs.isEmpty) + (Nil, "{invalid input}" :: Nil) + else + convertCompletions(cs) + } + + def convertCompletions(cs: Set[Completion]): (Seq[String], Seq[String]) = { + val (insert, display) = + ((Set.empty[String], Set.empty[String]) /: cs) { + case (t @ (insert, display), comp) => + if (comp.isEmpty) t else (insert + comp.append, appendNonEmpty(display, comp.display)) + } + (insert.toSeq, display.toSeq.sorted) + } + def appendNonEmpty(set: Set[String], add: String) = if (add.trim.isEmpty) set else set + add - def customCompletor(f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean = + def customCompletor( + f: (String, Int) => (Seq[String], Seq[String])): (ConsoleReader, Int) => Boolean = (reader, level) => { val success = complete(beforeCursor(reader), reader => f(reader, level), reader) reader.flush() success } - def bufferSnapshot(reader: ConsoleReader): (String, Int) = - { - val b = reader.getCursorBuffer - (b.buffer.toString, b.cursor) - } - def beforeCursor(reader: ConsoleReader): String = - { - val b = reader.getCursorBuffer - b.buffer.substring(0, b.cursor) - } + def bufferSnapshot(reader: ConsoleReader): (String, Int) = { + val b = reader.getCursorBuffer + (b.buffer.toString, b.cursor) + } + + def beforeCursor(reader: ConsoleReader): String = { + val b = reader.getCursorBuffer + b.buffer.substring(0, b.cursor) + } // returns false if there was nothing to insert and nothing to display - def complete(beforeCursor: String, completions: String => (Seq[String], Seq[String]), reader: ConsoleReader): Boolean = - { - val (insert, display) = completions(beforeCursor) - val common = commonPrefix(insert) - if (common.isEmpty) - if (display.isEmpty) - () - else - showCompletions(display, reader) + def complete( + beforeCursor: String, + completions: String => (Seq[String], Seq[String]), + reader: ConsoleReader + ): Boolean = { + val (insert, display) = completions(beforeCursor) + val common = commonPrefix(insert) + if (common.isEmpty) + if (display.isEmpty) + () else - appendCompletion(common, reader) + showCompletions(display, reader) + else + appendCompletion(common, reader) - !(common.isEmpty && display.isEmpty) - } + !(common.isEmpty && display.isEmpty) + } def appendCompletion(common: String, reader: ConsoleReader): Unit = { reader.getCursorBuffer.write(common) @@ -118,11 +136,13 @@ object JLineCompletion { printCompletions(display, reader) reader.drawLine() } + def printCompletions(cs: Seq[String], reader: ConsoleReader): Unit = { val print = shouldPrint(cs, reader) reader.println() if (print) printLinesAndColumns(cs, reader) } + def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader): Unit = { val (lines, columns) = cs partition hasNewline for (line <- lines) { @@ -132,26 +152,27 @@ object JLineCompletion { } reader.printColumns(JavaConversions.seqAsJavaList(columns.map(_.trim))) } + def hasNewline(s: String): Boolean = s.indexOf('\n') >= 0 - def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean = - { - val size = cs.size - (size <= reader.getAutoprintThreshold) || - confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader) - } - def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean = - { - reader.println() - reader.print(prompt) - reader.flush() - reader.readCharacter(trueC, falseC) == trueC - } + + def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean = { + val size = cs.size + (size <= reader.getAutoprintThreshold) || + confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader) + } + + def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean = { + reader.println() + reader.print(prompt) + reader.flush() + reader.readCharacter(trueC, falseC) == trueC + } def commonPrefix(s: Seq[String]): String = if (s.isEmpty) "" else s reduceLeft commonPrefix - def commonPrefix(a: String, b: String): String = - { - val len = scala.math.min(a.length, b.length) - @tailrec def loop(i: Int): Int = if (i >= len) len else if (a(i) != b(i)) i else loop(i + 1) - a.substring(0, loop(0)) - } + + def commonPrefix(a: String, b: String): String = { + val len = scala.math.min(a.length, b.length) + @tailrec def loop(i: Int): Int = if (i >= len) len else if (a(i) != b(i)) i else loop(i + 1) + a.substring(0, loop(0)) + } } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index 003862c5e..3ed65c2ee 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -24,7 +24,9 @@ sealed trait Parser[+T] { def ifValid[S](p: => Parser[S]): Parser[S] def valid: Boolean } + sealed trait RichParser[A] { + /** Apply the original Parser and then apply `next` (in order). The result of both is provides as a pair. */ def ~[B](next: Parser[B]): Parser[(A, B)] @@ -100,14 +102,19 @@ sealed trait RichParser[A] { * be displayed. * @return a new parser with a new source of completions. */ - def examples(exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] + def examples( + exampleSource: ExampleSource, + maxNumberOfExamples: Int, + removeInvalidExamples: Boolean + ): Parser[A] /** * @param exampleSource the source of examples when displaying completions to the user. * @return a new parser with a new source of completions. It displays at most 25 completion examples and does not * remove invalid examples. */ - def examples(exampleSource: ExampleSource): Parser[A] = examples(exampleSource, maxNumberOfExamples = 25, removeInvalidExamples = false) + def examples(exampleSource: ExampleSource): Parser[A] = + examples(exampleSource, maxNumberOfExamples = 25, removeInvalidExamples = false) /** Converts a Parser returning a Char sequence to a Parser returning a String.*/ def string(implicit ev: A <:< Seq[Char]): Parser[String] @@ -139,14 +146,17 @@ object Parser extends ParserMain { def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] def toEither: Either[() => Seq[String], T] } + final case class Value[+T](value: T) extends Result[T] { def isFailure = false def isValid: Boolean = true def errors = Nil + def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] = b match { case fail: Failure => fail case Value(bv) => Value(f(value, bv)) } + def &&(b: => Result[_]): Result[T] = b match { case f: Failure => f; case _ => this } def or[B >: T](b: => Result[B]): Result[B] = this def either[B](b: => Result[B]): Result[Either[T, B]] = Value(Left(value)) @@ -155,20 +165,25 @@ object Parser extends ParserMain { def filter(f: T => Boolean, msg: => String): Result[T] = if (f(value)) this else mkFailure(msg) def toEither = Right(value) } - final class Failure private[sbt] (mkErrors: => Seq[String], val definitive: Boolean) extends Result[Nothing] { + + final class Failure private[sbt] (mkErrors: => Seq[String], val definitive: Boolean) + extends Result[Nothing] { lazy val errors: Seq[String] = mkErrors def isFailure = true def isValid = false def map[B](f: Nothing => B) = this def flatMap[B](f: Nothing => Result[B]) = this + def or[B](b: => Result[B]): Result[B] = b match { case v: Value[B] => v case f: Failure => if (definitive) this else this ++ f } + def either[B](b: => Result[B]): Result[Either[Nothing, B]] = b match { case Value(v) => Value(Right(v)) case f: Failure => if (definitive) this else this ++ f } + def filter(f: Nothing => Boolean, msg: => String) = this def app[B, C](b: => Result[B])(f: (Nothing, B) => C): Result[C] = this def &&(b: => Result[_]) = this @@ -176,8 +191,12 @@ object Parser extends ParserMain { private[sbt] def ++(f: Failure) = mkFailures(errors ++ f.errors) } - def mkFailures(errors: => Seq[String], definitive: Boolean = false): Failure = new Failure(errors.distinct, definitive) - def mkFailure(error: => String, definitive: Boolean = false): Failure = new Failure(error :: Nil, definitive) + + def mkFailures(errors: => Seq[String], definitive: Boolean = false): Failure = + new Failure(errors.distinct, definitive) + + def mkFailure(error: => String, definitive: Boolean = false): Failure = + new Failure(error :: Nil, definitive) def tuple[A, B](a: Option[A], b: Option[B]): Option[(A, B)] = (a, b) match { case (Some(av), Some(bv)) => Some((av, bv)); case _ => None } @@ -198,7 +217,12 @@ object Parser extends ParserMain { } } - def filterParser[T](a: Parser[T], f: T => Boolean, seen: String, msg: String => String): Parser[T] = + def filterParser[T]( + a: Parser[T], + f: T => Boolean, + seen: String, + msg: String => String + ): Parser[T] = a.ifValid { a.result match { case Some(av) if f(av) => success(av) @@ -211,8 +235,8 @@ object Parser extends ParserMain { b.ifValid { (a.result, b.result) match { case (Some(av), Some(bv)) => success((av, bv)) - case (Some(av), None) => b map { bv => (av, bv) } - case (None, Some(bv)) => a map { av => (av, bv) } + case (Some(av), None) => b map (bv => (av, bv)) + case (None, Some(bv)) => a map (av => (av, bv)) case (None, None) => new SeqParser(a, b) } } @@ -229,6 +253,7 @@ object Parser extends ParserMain { def onFailure[T](delegate: Parser[T], msg: String): Parser[T] = if (delegate.valid) new OnFailure(delegate, msg) else failure(msg) + def trapAndFail[T](delegate: Parser[T]): Parser[T] = delegate.ifValid(new TrapAndFail(delegate)) @@ -237,37 +262,49 @@ object Parser extends ParserMain { def repeat[T](p: Parser[T], min: Int = 0, max: UpperBound = Infinite): Parser[Seq[T]] = repeat(None, p, min, max, Nil) - private[complete] def repeat[T](partial: Option[Parser[T]], repeated: Parser[T], min: Int, max: UpperBound, revAcc: List[T]): Parser[Seq[T]] = - { - assume(min >= 0, "Minimum must be greater than or equal to zero (was " + min + ")") - assume(max >= min, "Minimum must be less than or equal to maximum (min: " + min + ", max: " + max + ")") - def checkRepeated(invalidButOptional: => Parser[Seq[T]]): Parser[Seq[T]] = - repeated match { - case i: Invalid if min == 0 => invalidButOptional - case i: Invalid => i - case _ => - repeated.result match { - case Some(value) => success(revAcc reverse_::: value :: Nil) // revAcc should be Nil here - case None => if (max.isZero) success(revAcc.reverse) else new Repeat(partial, repeated, min, max, revAcc) - } - } + private[complete] def repeat[T]( + partial: Option[Parser[T]], + repeated: Parser[T], + min: Int, + max: UpperBound, + revAcc: List[T] + ): Parser[Seq[T]] = { + assume(min >= 0, "Minimum must be greater than or equal to zero (was " + min + ")") + assume(max >= min, + "Minimum must be less than or equal to maximum (min: " + min + ", max: " + max + ")") - partial match { - case Some(part) => - part.ifValid { - part.result match { - case Some(value) => repeat(None, repeated, min, max, value :: revAcc) - case None => checkRepeated(part.map(lv => (lv :: revAcc).reverse)) - } + def checkRepeated(invalidButOptional: => Parser[Seq[T]]): Parser[Seq[T]] = + repeated match { + case i: Invalid if min == 0 => invalidButOptional + case i: Invalid => i + case _ => + repeated.result match { + case Some(value) => + success(revAcc reverse_::: value :: Nil) // revAcc should be Nil here + case None => + if (max.isZero) success(revAcc.reverse) + else new Repeat(partial, repeated, min, max, revAcc) } - case None => checkRepeated(success(Nil)) } + + partial match { + case Some(part) => + part.ifValid { + part.result match { + case Some(value) => repeat(None, repeated, min, max, value :: revAcc) + case None => checkRepeated(part.map(lv => (lv :: revAcc).reverse)) + } + } + case None => checkRepeated(success(Nil)) } + } def and[T](a: Parser[T], b: Parser[_]): Parser[T] = a.ifValid(b.ifValid(new And(a, b))) } + trait ParserMain { + /** Provides combinators for Parsers.*/ implicit def richParser[A](a: Parser[A]): RichParser[A] = new RichParser[A] { def ~[B](b: Parser[B]) = seqParser(a, b) @@ -279,7 +316,7 @@ trait ParserMain { def map[B](f: A => B) = mapParser(a, f) def id = a - def ^^^[B](value: B): Parser[B] = a map { _ => value } + def ^^^[B](value: B): Parser[B] = a map (_ => value) def ??[B >: A](alt: B): Parser[B] = a.? map { _ getOrElse alt } def <~[B](b: Parser[B]): Parser[A] = (a ~ b) map { case av ~ _ => av } def ~>[B](b: Parser[B]): Parser[B] = (a ~ b) map { case _ ~ bv => bv } @@ -290,8 +327,17 @@ trait ParserMain { def &(o: Parser[_]) = and(a, o) def -(o: Parser[_]) = and(a, not(o, "Unexpected: " + o)) def examples(s: String*): Parser[A] = examples(s.toSet) - def examples(s: Set[String], check: Boolean = false): Parser[A] = examples(new FixedSetExamples(s), s.size, check) - def examples(s: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] = Parser.examples(a, s, maxNumberOfExamples, removeInvalidExamples) + + def examples(s: Set[String], check: Boolean = false): Parser[A] = + examples(new FixedSetExamples(s), s.size, check) + + def examples( + s: ExampleSource, + maxNumberOfExamples: Int, + removeInvalidExamples: Boolean + ): Parser[A] = + Parser.examples(a, s, maxNumberOfExamples, removeInvalidExamples) + def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg) def string(implicit ev: A <:< Seq[Char]): Parser[String] = map(_.mkString) def flatMap[B](f: A => Parser[B]) = bindParser(a, f) @@ -313,13 +359,15 @@ trait ParserMain { * Defines a parser that always fails on any input with messages `msgs`. * If `definitive` is `true`, any failures by later alternatives are discarded. */ - def invalid(msgs: => Seq[String], definitive: Boolean = false): Parser[Nothing] = Invalid(mkFailures(msgs, definitive)) + def invalid(msgs: => Seq[String], definitive: Boolean = false): Parser[Nothing] = + Invalid(mkFailures(msgs, definitive)) /** * Defines a parser that always fails on any input with message `msg`. * If `definitive` is `true`, any failures by later alternatives are discarded. */ - def failure(msg: => String, definitive: Boolean = false): Parser[Nothing] = invalid(msg :: Nil, definitive) + def failure(msg: => String, definitive: Boolean = false): Parser[Nothing] = + invalid(msg :: Nil, definitive) /** Defines a parser that always succeeds on empty input with the result `value`.*/ def success[T](value: T): Parser[T] = new ValidParser[T] { @@ -335,17 +383,17 @@ trait ParserMain { charClass(r contains _).examples(r.map(_.toString): _*) /** Defines a Parser that parses a single character only if it is contained in `legal`.*/ - def chars(legal: String): Parser[Char] = - { - val set = legal.toSet - charClass(set, "character in '" + legal + "'") examples (set.map(_.toString)) - } + def chars(legal: String): Parser[Char] = { + val set = legal.toSet + charClass(set, "character in '" + legal + "'") examples (set.map(_.toString)) + } /** * Defines a Parser that parses a single character only if the predicate `f` returns true for that character. * If this parser fails, `label` is used as the failure message. */ - def charClass(f: Char => Boolean, label: String = ""): Parser[Char] = new CharacterClass(f, label) + def charClass(f: Char => Boolean, label: String = ""): Parser[Char] = + new CharacterClass(f, label) /** Presents a single Char `ch` as a Parser that only parses that exact character. */ implicit def literal(ch: Char): Parser[Char] = new ValidParser[Char] { @@ -355,13 +403,16 @@ trait ParserMain { def completions(level: Int) = Completions.single(Completion.suggestion(ch.toString)) override def toString = "'" + ch + "'" } + /** Presents a literal String `s` as a Parser that only parses that exact text and provides it as the result.*/ implicit def literal(s: String): Parser[String] = stringLiteral(s, 0) /** See [[unapply]]. */ object ~ { + /** Convenience for destructuring a tuple that mirrors the `~` combinator.*/ def unapply[A, B](t: (A, B)): Some[(A, B)] = Some(t) + } /** Parses input `str` using `parser`. If successful, the result is provided wrapped in `Right`. If unsuccessful, an error message is provided in `Left`.*/ @@ -403,24 +454,22 @@ trait ParserMain { Parser.completions(parser, str, level).get foreach println // intended to be temporary pending proper error feedback - def result[T](p: Parser[T], s: String): Either[() => (Seq[String], Int), T] = - { - def loop(i: Int, a: Parser[T]): Either[() => (Seq[String], Int), T] = - a match { - case Invalid(f) => Left(() => (f.errors, i)) - case _ => - val ci = i + 1 - if (ci >= s.length) - a.resultEmpty.toEither.left.map { msgs0 => () => - val msgs = msgs0() - val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs - (nonEmpty, ci) - } - else - loop(ci, a derive s(ci)) - } - loop(-1, p) - } + def result[T](p: Parser[T], s: String): Either[() => (Seq[String], Int), T] = { + def loop(i: Int, a: Parser[T]): Either[() => (Seq[String], Int), T] = + a match { + case Invalid(f) => Left(() => (f.errors, i)) + case _ => + val ci = i + 1 + if (ci >= s.length) + a.resultEmpty.toEither.left.map { msgs0 => () => + val msgs = msgs0() + val nonEmpty = if (msgs.isEmpty) "Unexpected end of input" :: Nil else msgs + (nonEmpty, ci) + } else + loop(ci, a derive s(ci)) + } + loop(-1, p) + } /** Applies parser `p` to input `s`. */ def apply[T](p: Parser[T])(s: String): Parser[T] = @@ -454,7 +503,12 @@ trait ParserMain { * @tparam A the type of values that are returned by the parser. * @return */ - def examples[A](a: Parser[A], completions: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean): Parser[A] = + def examples[A]( + a: Parser[A], + completions: ExampleSource, + maxNumberOfExamples: Int, + removeInvalidExamples: Boolean + ): Parser[A] = if (a.valid) { a.result match { case Some(av) => success(av) @@ -463,7 +517,11 @@ trait ParserMain { } } else a - def matched(t: Parser[_], seen: Vector[Char] = Vector.empty, partial: Boolean = false): Parser[String] = + def matched( + t: Parser[_], + seen: Vector[Char] = Vector.empty, + partial: Boolean = false + ): Parser[String] = t match { case i: Invalid => if (partial && seen.nonEmpty) success(seen.mkString) else i case _ => @@ -485,13 +543,15 @@ trait ParserMain { * When tab completion of part of this token is requested, no completions are returned if `hide` returns true for the current tab completion level. * Otherwise, the completions provided by the delegate `t` or a later derivative are appended to the prefix String already seen by this parser. */ - def token[T](t: Parser[T], hide: Int => Boolean): Parser[T] = token(t, TokenCompletions.default.hideWhen(hide)) + def token[T](t: Parser[T], hide: Int => Boolean): Parser[T] = + token(t, TokenCompletions.default.hideWhen(hide)) /** * Establishes delegate parser `t` as a single token of tab completion. * When tab completion of part of this token is requested, `description` is displayed for suggestions and no completions are ever performed. */ - def token[T](t: Parser[T], description: String): Parser[T] = token(t, TokenCompletions.displayOnly(description)) + def token[T](t: Parser[T], description: String): Parser[T] = + token(t, TokenCompletions.displayOnly(description)) /** * Establishes delegate parser `t` as a single token of tab completion. @@ -524,24 +584,29 @@ trait ParserMain { def oneOf[T](p: Seq[Parser[T]]): Parser[T] = p.reduceLeft(_ | _) def seq[T](p: Seq[Parser[T]]): Parser[Seq[T]] = seq0(p, Nil) - def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] = - { - val (newErrors, valid) = separate(p) { case Invalid(f) => Left(f.errors _); case ok => Right(ok) } - def combinedErrors = errors ++ newErrors.flatMap(_()) - if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors) - } - def stringLiteral(s: String, start: Int): Parser[String] = - { - val len = s.length - if (len == 0) sys.error("String literal cannot be empty") else if (start >= len) success(s) else new StringLiteral(s, start) + def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] = { + val (newErrors, valid) = separate(p) { + case Invalid(f) => Left(f.errors _); case ok => Right(ok) } + def combinedErrors = errors ++ newErrors.flatMap(_()) + if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors) + } + + def stringLiteral(s: String, start: Int): Parser[String] = { + val len = s.length + if (len == 0) sys.error("String literal cannot be empty") + else if (start >= len) success(s) + else new StringLiteral(s, start) + } } + sealed trait ValidParser[T] extends Parser[T] { final def valid = true final def failure = None final def ifValid[S](p: => Parser[S]): Parser[S] = p } + private final case class Invalid(fail: Failure) extends Parser[Nothing] { def failure = Some(fail) def result = None @@ -562,10 +627,17 @@ private final case class SoftInvalid(fail: Failure) extends ValidParser[Nothing] } private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A] { - def result = try { a.result } catch { case e: Exception => None } + def result = try { a.result } catch { case e: Exception => None } def resultEmpty = try { a.resultEmpty } catch { case e: Exception => fail(e) } - def derive(c: Char) = try { trapAndFail(a derive c) } catch { case e: Exception => Invalid(fail(e)) } - def completions(level: Int) = try { a.completions(level) } catch { case e: Exception => Completions.nil } + + def derive(c: Char) = try { trapAndFail(a derive c) } catch { + case e: Exception => Invalid(fail(e)) + } + + def completions(level: Int) = try { a.completions(level) } catch { + case e: Exception => Completions.nil + } + override def toString = "trap(" + a + ")" override def isTokenStart = a.isTokenStart private[this] def fail(e: Exception): Failure = mkFailure(e.toString) @@ -573,23 +645,29 @@ private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A] { private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A] { def result = a.result - def resultEmpty = a.resultEmpty match { case f: Failure => mkFailure(message); case v: Value[A] => v } + + def resultEmpty = a.resultEmpty match { + case f: Failure => mkFailure(message); case v: Value[A] => v + } + def derive(c: Char) = onFailure(a derive c, message) def completions(level: Int) = a.completions(level) override def toString = "(" + a + " !!! \"" + message + "\" )" override def isTokenStart = a.isTokenStart } + private final class SeqParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[(A, B)] { lazy val result = tuple(a.result, b.result) lazy val resultEmpty = a.resultEmpty seq b.resultEmpty - def derive(c: Char) = - { - val common = a.derive(c) ~ b - a.resultEmpty match { - case Value(av) => common | b.derive(c).map(br => (av, br)) - case _: Failure => common - } + + def derive(c: Char) = { + val common = a.derive(c) ~ b + a.resultEmpty match { + case Value(av) => common | b.derive(c).map(br => (av, br)) + case _: Failure => common } + } + def completions(level: Int) = a.completions(level) x b.completions(level) override def toString = "(" + a + " ~ " + b + ")" } @@ -601,6 +679,7 @@ private final class HomParser[A](a: Parser[A], b: Parser[A]) extends ValidParser def completions(level: Int) = a.completions(level) ++ b.completions(level) override def toString = "(" + a + " | " + b + ")" } + private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[Either[A, B]] { lazy val result = tuple(a.result, b.result) map { case (a, b) => Left(a) } def derive(c: Char) = (a derive c) || (b derive c) @@ -608,27 +687,33 @@ private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidPar def completions(level: Int) = a.completions(level) ++ b.completions(level) override def toString = "(" + a + " || " + b + ")" } -private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) extends ValidParser[Seq[T]] { + +private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) + extends ValidParser[Seq[T]] { assert(a.nonEmpty) - lazy val resultEmpty: Result[Seq[T]] = - { - val res = a.map(_.resultEmpty) - val (failures, values) = separate(res)(_.toEither) - // if(failures.isEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) - if (values.nonEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) - } + + lazy val resultEmpty: Result[Seq[T]] = { + val res = a.map(_.resultEmpty) + val (failures, values) = separate(res)(_.toEither) + // if(failures.isEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) + if (values.nonEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) + } + def result = { val success = a.flatMap(_.result) if (success.length == a.length) Some(success) else None } + def completions(level: Int) = a.map(_.completions(level)).reduceLeft(_ ++ _) def derive(c: Char) = seq0(a.map(_ derive c), errors) + override def toString = "seq(" + a + ")" } private final class BindParser[A, B](a: Parser[A], f: A => Parser[B]) extends ValidParser[B] { - lazy val result = a.result flatMap { av => f(av).result } - lazy val resultEmpty = a.resultEmpty flatMap { av => f(av).resultEmpty } + lazy val result = a.result flatMap (av => f(av).result) + lazy val resultEmpty = a.resultEmpty flatMap (av => f(av).resultEmpty) + def completions(level: Int) = a.completions(level) flatMap { c => apply(a)(c.append).resultEmpty match { @@ -637,17 +722,19 @@ private final class BindParser[A, B](a: Parser[A], f: A => Parser[B]) extends Va } } - def derive(c: Char) = - { - val common = a derive c flatMap f - a.resultEmpty match { - case Value(av) => common | derive1(f(av), c) - case _: Failure => common - } + def derive(c: Char) = { + val common = a derive c flatMap f + a.resultEmpty match { + case Value(av) => common | derive1(f(av), c) + case _: Failure => common } + } + override def isTokenStart = a.isTokenStart + override def toString = "bind(" + a + ")" } + private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser[B] { lazy val result = a.result map f lazy val resultEmpty = a.resultEmpty map f @@ -656,35 +743,53 @@ private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser override def isTokenStart = a.isTokenStart override def toString = "map(" + a + ")" } -private final class Filter[T](p: Parser[T], f: T => Boolean, seen: String, msg: String => String) extends ValidParser[T] { + +private final class Filter[T](p: Parser[T], f: T => Boolean, seen: String, msg: String => String) + extends ValidParser[T] { def filterResult(r: Result[T]) = r.filter(f, msg(seen)) lazy val result = p.result filter f lazy val resultEmpty = filterResult(p.resultEmpty) def derive(c: Char) = filterParser(p derive c, f, seen + c, msg) - def completions(level: Int) = p.completions(level) filterS { s => filterResult(apply(p)(s).resultEmpty).isValid } + + def completions(level: Int) = p.completions(level) filterS { s => + filterResult(apply(p)(s).resultEmpty).isValid + } + override def toString = "filter(" + p + ")" override def isTokenStart = p.isTokenStart } -private final class MatchedString(delegate: Parser[_], seenV: Vector[Char], partial: Boolean) extends ValidParser[String] { + +private final class MatchedString(delegate: Parser[_], seenV: Vector[Char], partial: Boolean) + extends ValidParser[String] { lazy val seen = seenV.mkString def derive(c: Char) = matched(delegate derive c, seenV :+ c, partial) def completions(level: Int) = delegate.completions(level) def result = if (delegate.result.isDefined) Some(seen) else None - def resultEmpty = delegate.resultEmpty match { case f: Failure if !partial => f; case _ => Value(seen) } + + def resultEmpty = delegate.resultEmpty match { + case f: Failure if !partial => f; case _ => Value(seen) + } + override def isTokenStart = delegate.isTokenStart override def toString = "matched(" + partial + ", " + seen + ", " + delegate + ")" } -private final class TokenStart[T](delegate: Parser[T], seen: String, complete: TokenCompletions) extends ValidParser[T] { + +private final class TokenStart[T](delegate: Parser[T], seen: String, complete: TokenCompletions) + extends ValidParser[T] { def derive(c: Char) = mkToken(delegate derive c, seen + c, complete) + def completions(level: Int) = complete match { - case dc: TokenCompletions.Delegating => dc.completions(seen, level, delegate.completions(level)) - case fc: TokenCompletions.Fixed => fc.completions(seen, level) + case dc: TokenCompletions.Delegating => + dc.completions(seen, level, delegate.completions(level)) + case fc: TokenCompletions.Fixed => fc.completions(seen, level) } + def result = delegate.result def resultEmpty = delegate.resultEmpty override def isTokenStart = true override def toString = "token('" + complete + ", " + delegate + ")" } + private final class And[T](a: Parser[T], b: Parser[_]) extends ValidParser[T] { lazy val result = tuple(a.result, b.result) map { _._1 } def derive(c: Char) = (a derive c) & (b derive c) @@ -697,10 +802,12 @@ private final class Not(delegate: Parser[_], failMessage: String) extends ValidP def derive(c: Char) = if (delegate.valid) not(delegate derive c, failMessage) else this def completions(level: Int) = Completions.empty def result = None + lazy val resultEmpty = delegate.resultEmpty match { case f: Failure => Value(()) case v: Value[_] => mkFailure(failMessage) } + override def toString = " -(%s)".format(delegate) } @@ -719,9 +826,18 @@ private final class Not(delegate: Parser[_], failMessage: String) extends ValidP * @param removeInvalidExamples indicates whether to remove examples that are deemed invalid by the delegate parser. * @tparam T the type of value produced by the parser. */ -private final class ParserWithExamples[T](delegate: Parser[T], exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean) extends ValidParser[T] { +private final class ParserWithExamples[T]( + delegate: Parser[T], + exampleSource: ExampleSource, + maxNumberOfExamples: Int, + removeInvalidExamples: Boolean +) extends ValidParser[T] { + def derive(c: Char) = - examples(delegate derive c, exampleSource.withAddedPrefix(c.toString), maxNumberOfExamples, removeInvalidExamples) + examples(delegate derive c, + exampleSource.withAddedPrefix(c.toString), + maxNumberOfExamples, + removeInvalidExamples) def result = delegate.result @@ -749,15 +865,21 @@ private final class ParserWithExamples[T](delegate: Parser[T], exampleSource: Ex apply(delegate)(example).resultEmpty.isValid } } + private final class StringLiteral(str: String, start: Int) extends ValidParser[String] { assert(0 <= start && start < str.length) + def failMsg = "Expected '" + str + "'" def resultEmpty = mkFailure(failMsg) def result = None - def derive(c: Char) = if (str.charAt(start) == c) stringLiteral(str, start + 1) else new Invalid(resultEmpty) + + def derive(c: Char) = + if (str.charAt(start) == c) stringLiteral(str, start + 1) else new Invalid(resultEmpty) + def completions(level: Int) = Completions.single(Completion.suggestion(str.substring(start))) override def toString = '"' + str + '"' } + private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char] { def result = None def resultEmpty = mkFailure("Expected " + label) @@ -765,6 +887,7 @@ private final class CharacterClass(f: Char => Boolean, label: String) extends Va def completions(level: Int) = Completions.empty override def toString = "class(" + label + ")" } + private final class Optional[T](delegate: Parser[T]) extends ValidParser[Option[T]] { def result = delegate.result map some.fn def resultEmpty = Value(None) @@ -772,7 +895,14 @@ private final class Optional[T](delegate: Parser[T]) extends ValidParser[Option[ def completions(level: Int) = Completion.empty +: delegate.completions(level) override def toString = delegate.toString + "?" } -private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], min: Int, max: UpperBound, accumulatedReverse: List[T]) extends ValidParser[Seq[T]] { + +private final class Repeat[T]( + partial: Option[Parser[T]], + repeated: Parser[T], + min: Int, + max: UpperBound, + accumulatedReverse: List[T] +) extends ValidParser[Seq[T]] { assume(0 <= min, "Minimum occurences must be non-negative") assume(max >= min, "Minimum occurences must be less than the maximum occurences") @@ -787,37 +917,39 @@ private final class Repeat[T](partial: Option[Parser[T]], repeated: Parser[T], m case None => repeatDerive(c, accumulatedReverse) } - def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = repeat(Some(repeated derive c), repeated, scala.math.max(0, min - 1), max.decrement, accRev) + def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = + repeat(Some(repeated derive c), repeated, scala.math.max(0, min - 1), max.decrement, accRev) - def completions(level: Int) = - { - def pow(comp: Completions, exp: Completions, n: Int): Completions = - if (n == 1) comp else pow(comp x exp, exp, n - 1) + def completions(level: Int) = { + def pow(comp: Completions, exp: Completions, n: Int): Completions = + if (n == 1) comp else pow(comp x exp, exp, n - 1) - val repC = repeated.completions(level) - val fin = if (min == 0) Completion.empty +: repC else pow(repC, repC, min) - partial match { - case Some(p) => p.completions(level) x fin - case None => fin - } + val repC = repeated.completions(level) + val fin = if (min == 0) Completion.empty +: repC else pow(repC, repC, min) + partial match { + case Some(p) => p.completions(level) x fin + case None => fin } + } + def result = None - lazy val resultEmpty: Result[Seq[T]] = - { - val partialAccumulatedOption = - partial match { - case None => Value(accumulatedReverse) - case Some(partialPattern) => partialPattern.resultEmpty.map(_ :: accumulatedReverse) - } - (partialAccumulatedOption app repeatedParseEmpty)(_ reverse_::: _) - } - private def repeatedParseEmpty: Result[List[T]] = - { - if (min == 0) - Value(Nil) - else - // forced determinism - for (value <- repeated.resultEmpty) yield makeList(min, value) - } + + lazy val resultEmpty: Result[Seq[T]] = { + val partialAccumulatedOption = + partial match { + case None => Value(accumulatedReverse) + case Some(partialPattern) => partialPattern.resultEmpty.map(_ :: accumulatedReverse) + } + (partialAccumulatedOption app repeatedParseEmpty)(_ reverse_::: _) + } + + private def repeatedParseEmpty: Result[List[T]] = { + if (min == 0) + Value(Nil) + else + // forced determinism + for (value <- repeated.resultEmpty) yield makeList(min, value) + } + override def toString = "repeat(" + min + "," + max + "," + partial + "," + repeated + ")" } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala index 9463d1acb..55dea1033 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala @@ -7,10 +7,19 @@ package complete import Parser._ import java.io.File import java.net.URI -import java.lang.Character.{ getType, MATH_SYMBOL, OTHER_SYMBOL, DASH_PUNCTUATION, OTHER_PUNCTUATION, MODIFIER_SYMBOL, CURRENCY_SYMBOL } +import java.lang.Character.{ + getType, + MATH_SYMBOL, + OTHER_SYMBOL, + DASH_PUNCTUATION, + OTHER_PUNCTUATION, + MODIFIER_SYMBOL, + CURRENCY_SYMBOL +} /** Provides standard implementations of commonly useful [[Parser]]s. */ trait Parsers { + /** Matches the end of input, providing no useful result on success. */ lazy val EOF = not(any, "Expected EOF") @@ -24,10 +33,12 @@ trait Parsers { lazy val Digit = charClass(_.isDigit, "digit") examples DigitSet /** Set containing Chars for hexadecimal digits 0-9 and A-F (but not a-f). */ - lazy val HexDigitSet = Set('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F') + lazy val HexDigitSet = + Set('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F') /** Parses a single hexadecimal digit (0-9, a-f, A-F). */ - lazy val HexDigit = charClass(c => HexDigitSet(c.toUpper), "hex digit") examples HexDigitSet.map(_.toString) + lazy val HexDigit = charClass(c => HexDigitSet(c.toUpper), "hex digit") examples HexDigitSet.map( + _.toString) /** Parses a single letter, according to Char.isLetter, into a Char. */ lazy val Letter = charClass(_.isLetter, "letter") @@ -70,14 +81,22 @@ trait Parsers { /** Returns true if `c` an operator character. */ def isOpChar(c: Char) = !isDelimiter(c) && isOpType(getType(c)) - def isOpType(cat: Int) = cat match { case MATH_SYMBOL | OTHER_SYMBOL | DASH_PUNCTUATION | OTHER_PUNCTUATION | MODIFIER_SYMBOL | CURRENCY_SYMBOL => true; case _ => false } + + def isOpType(cat: Int) = cat match { + case MATH_SYMBOL | OTHER_SYMBOL | DASH_PUNCTUATION | OTHER_PUNCTUATION | MODIFIER_SYMBOL | + CURRENCY_SYMBOL => + true; case _ => false + } + /** Returns true if `c` is a dash `-`, a letter, digit, or an underscore `_`. */ def isIDChar(c: Char) = isScalaIDChar(c) || c == '-' /** Returns true if `c` is a letter, digit, or an underscore `_`. */ def isScalaIDChar(c: Char) = c.isLetterOrDigit || c == '_' - def isDelimiter(c: Char) = c match { case '`' | '\'' | '\"' | /*';' | */ ',' | '.' => true; case _ => false } + def isDelimiter(c: Char) = c match { + case '`' | '\'' | '\"' | /*';' | */ ',' | '.' => true; case _ => false + } /** Matches a single character that is not a whitespace character. */ lazy val NotSpaceClass = charClass(!_.isWhitespace, "non-whitespace character") @@ -120,17 +139,22 @@ trait Parsers { /** Matches any character except a double quote or whitespace. */ lazy val NotDQuoteSpaceClass = - charClass({ c: Char => (c != DQuoteChar) && !c.isWhitespace }, "non-double-quote-space character") + charClass({ c: Char => + (c != DQuoteChar) && !c.isWhitespace + }, "non-double-quote-space character") /** Matches any character except a double quote or backslash. */ lazy val NotDQuoteBackslashClass = - charClass({ c: Char => (c != DQuoteChar) && (c != BackslashChar) }, "non-double-quote-backslash character") + charClass({ c: Char => + (c != DQuoteChar) && (c != BackslashChar) + }, "non-double-quote-backslash character") /** Matches a single character that is valid somewhere in a URI. */ lazy val URIChar = charClass(alphanum) | chars("_-!.~'()*,;:$&+=?/[]@%#") /** Returns true if `c` is an ASCII letter or digit. */ - def alphanum(c: Char) = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') + def alphanum(c: Char) = + ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') /** * @param base the directory used for completion proposals (when the user presses the TAB key). Only paths under this @@ -192,7 +216,9 @@ trait Parsers { * A unicode escape begins with a backslash, followed by a `u` and 4 hexadecimal digits representing the unicode value. */ lazy val UnicodeEscape: Parser[Char] = - ("u" ~> repeat(HexDigit, 4, 4)) map { seq => Integer.parseInt(seq.mkString, 16).toChar } + ("u" ~> repeat(HexDigit, 4, 4)) map { seq => + Integer.parseInt(seq.mkString, 16).toChar + } /** Parses an unquoted, non-empty String value that cannot start with a double quote and cannot contain whitespace.*/ lazy val NotQuoted = (NotDQuoteSpaceClass ~ OptNotSpace) map { case (c, s) => c.toString + s } @@ -212,20 +238,25 @@ trait Parsers { (rep ~ (sep ~> rep).*).map { case (x ~ xs) => x +: xs } /** Wraps the result of `p` in `Some`.*/ - def some[T](p: Parser[T]): Parser[Option[T]] = p map { v => Some(v) } + def some[T](p: Parser[T]): Parser[Option[T]] = p map { v => + Some(v) + } /** * Applies `f` to the result of `p`, transforming any exception when evaluating * `f` into a parse failure with the exception `toString` as the message. */ def mapOrFail[S, T](p: Parser[S])(f: S => T): Parser[T] = - p flatMap { s => try { success(f(s)) } catch { case e: Exception => failure(e.toString) } } + p flatMap { s => + try { success(f(s)) } catch { case e: Exception => failure(e.toString) } + } /** * Parses a space-delimited, possibly empty sequence of arguments. * The arguments may use quotes and escapes according to [[StringBasic]]. */ - def spaceDelimited(display: String): Parser[Seq[String]] = (token(Space) ~> token(StringBasic, display)).* <~ SpaceClass.* + def spaceDelimited(display: String): Parser[Seq[String]] = + (token(Space) ~> token(StringBasic, display)).* <~ SpaceClass.* /** Applies `p` and uses `true` as the result if it succeeds and turns failure into a result of `false`. */ def flag[T](p: Parser[T]): Parser[Boolean] = (p ^^^ true) ?? false @@ -236,14 +267,17 @@ trait Parsers { * The parsers obtained in this way are separated by `sep`, whose result is discarded and only the sequence * of values from the parsers returned by `p` is used for the result. */ - def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] = - { - def loop(acc: Seq[A]): Parser[Seq[A]] = { - val next = (sep ~> p(acc)) flatMap { result => loop(acc :+ result) } - next ?? acc + def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] = { + def loop(acc: Seq[A]): Parser[Seq[A]] = { + val next = (sep ~> p(acc)) flatMap { result => + loop(acc :+ result) } - p(Vector()) flatMap { first => loop(Seq(first)) } + next ?? acc } + p(Vector()) flatMap { first => + loop(Seq(first)) + } + } /** Applies String.trim to the result of `p`. */ def trimmed(p: Parser[String]) = p map { _.trim } @@ -260,10 +294,12 @@ object Parsers extends Parsers /** Provides common [[Parser]] implementations and helper methods.*/ object DefaultParsers extends Parsers with ParserMain { + /** Applies parser `p` to input `s` and returns `true` if the parse was successful. */ def matches(p: Parser[_], s: String): Boolean = apply(p)(s).resultEmpty.isValid /** Returns `true` if `s` parses successfully according to [[ID]].*/ def validID(s: String): Boolean = matches(ID, s) + } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala index 6d74ed2d2..6f8efff7e 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala @@ -2,29 +2,30 @@ package sbt.internal.util package complete object ProcessError { - def apply(command: String, msgs: Seq[String], index: Int): String = - { - val (line, modIndex) = extractLine(command, index) - val point = pointerSpace(command, modIndex) - msgs.mkString("\n") + "\n" + line + "\n" + point + "^" - } - def extractLine(s: String, i: Int): (String, Int) = - { - val notNewline = (c: Char) => c != '\n' && c != '\r' - val left = takeRightWhile(s.substring(0, i))(notNewline) - val right = s substring i takeWhile notNewline - (left + right, left.length) - } - def takeRightWhile(s: String)(pred: Char => Boolean): String = - { - def loop(i: Int): String = - if (i < 0) - s - else if (pred(s(i))) - loop(i - 1) - else - s.substring(i + 1) - loop(s.length - 1) - } - def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString "" + def apply(command: String, msgs: Seq[String], index: Int): String = { + val (line, modIndex) = extractLine(command, index) + val point = pointerSpace(command, modIndex) + msgs.mkString("\n") + "\n" + line + "\n" + point + "^" + } + + def extractLine(s: String, i: Int): (String, Int) = { + val notNewline = (c: Char) => c != '\n' && c != '\r' + val left = takeRightWhile(s.substring(0, i))(notNewline) + val right = s substring i takeWhile notNewline + (left + right, left.length) + } + + def takeRightWhile(s: String)(pred: Char => Boolean): String = { + def loop(i: Int): String = + if (i < 0) + s + else if (pred(s(i))) + loop(i - 1) + else + s.substring(i + 1) + loop(s.length - 1) + } + + def pointerSpace(s: String, i: Int): String = + (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString "" } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala index 0d0b2980e..512acfbec 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala @@ -6,6 +6,7 @@ import Completion.{ token => ctoken, tokenDisplay } sealed trait TokenCompletions { def hideWhen(f: Int => Boolean): TokenCompletions } + object TokenCompletions { private[sbt] abstract class Delegating extends TokenCompletions { outer => def completions(seen: String, level: Int, delegate: Completions): Completions @@ -14,6 +15,7 @@ object TokenCompletions { if (hide(level)) Completions.nil else outer.completions(seen, level, delegate) } } + private[sbt] abstract class Fixed extends TokenCompletions { outer => def completions(seen: String, level: Int): Completions final def hideWhen(hide: Int => Boolean): TokenCompletions = new Fixed { @@ -22,17 +24,23 @@ object TokenCompletions { } } - val default: TokenCompletions = mapDelegateCompletions((seen, level, c) => ctoken(seen, c.append)) + val default: TokenCompletions = mapDelegateCompletions( + (seen, level, c) => ctoken(seen, c.append)) def displayOnly(msg: String): TokenCompletions = new Fixed { def completions(seen: String, level: Int) = Completions.single(Completion.displayOnly(msg)) } - def overrideDisplay(msg: String): TokenCompletions = mapDelegateCompletions((seen, level, c) => tokenDisplay(display = msg, append = c.append)) + + def overrideDisplay(msg: String): TokenCompletions = + mapDelegateCompletions((seen, level, c) => tokenDisplay(display = msg, append = c.append)) def fixed(f: (String, Int) => Completions): TokenCompletions = new Fixed { def completions(seen: String, level: Int) = f(seen, level) } - def mapDelegateCompletions(f: (String, Int, Completion) => Completion): TokenCompletions = new Delegating { - def completions(seen: String, level: Int, delegate: Completions) = Completions(delegate.get.map(c => f(seen, level, c))) - } + + def mapDelegateCompletions(f: (String, Int, Completion) => Completion): TokenCompletions = + new Delegating { + def completions(seen: String, level: Int, delegate: Completions) = + Completions(delegate.get.map(c => f(seen, level, c))) + } } diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala index e96dbad4f..fa16073c0 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala @@ -20,6 +20,7 @@ private[sbt] final class TypeString(val base: String, val args: List[TypeString] } private[sbt] object TypeString { + /** Makes the string representation of a type as returned by Manifest.toString more readable.*/ def cleanup(typeString: String): String = parse(typeString, typeStringParser) match { @@ -59,6 +60,7 @@ private[sbt] object TypeString { final val JavaPrefix = "java.lang." /* scala.collection.X -> X */ val ShortenCollection = Set("Seq", "List", "Set", "Map", "Iterable") + val TypeMap = Map( "java.io.File" -> "File", "java.net.URL" -> "URL", @@ -69,12 +71,13 @@ private[sbt] object TypeString { * A Parser that extracts basic structure from the string representation of a type from Manifest.toString. * This is rudimentary and essentially only decomposes the string into names and arguments for parameterized types. */ - lazy val typeStringParser: Parser[TypeString] = - { - def isFullScalaIDChar(c: Char) = isScalaIDChar(c) || c == '.' || c == '$' - lazy val fullScalaID = identifier(IDStart, charClass(isFullScalaIDChar, "Scala identifier character")) - lazy val tpe: Parser[TypeString] = - for (id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').?) yield new TypeString(id, args.toList.flatten) - tpe - } -} \ No newline at end of file + lazy val typeStringParser: Parser[TypeString] = { + def isFullScalaIDChar(c: Char) = isScalaIDChar(c) || c == '.' || c == '$' + lazy val fullScalaID = + identifier(IDStart, charClass(isFullScalaIDChar, "Scala identifier character")) + lazy val tpe: Parser[TypeString] = + for (id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').?) + yield new TypeString(id, args.toList.flatten) + tpe + } +} diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala index 6b600f9ed..e76d3b102 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala @@ -5,30 +5,41 @@ package sbt.internal.util package complete sealed trait UpperBound { + /** True if and only if the given value meets this bound.*/ def >=(min: Int): Boolean + /** True if and only if this bound is one.*/ def isOne: Boolean + /** True if and only if this bound is zero.*/ def isZero: Boolean + /** * If this bound is zero or Infinite, `decrement` returns this bound. * Otherwise, this bound is finite and greater than zero and `decrement` returns the bound that is one less than this bound. */ def decrement: UpperBound + /** True if and only if this is unbounded.*/ def isInfinite: Boolean + } + /** Represents unbounded. */ case object Infinite extends UpperBound { + /** All finite numbers meet this bound. */ def >=(min: Int) = true + def isOne = false def isZero = false def decrement = this def isInfinite = true + override def toString = "Infinity" } + /** * Represents a finite upper bound. The maximum allowed value is 'value', inclusive. * It must positive. @@ -43,6 +54,7 @@ final case class Finite(value: Int) extends UpperBound { def isInfinite = false override def toString = value.toString } + object UpperBound { implicit def intToFinite(i: Int): Finite = Finite(i) } diff --git a/internal/util-complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala index 1db99b513..f3abdbe50 100644 --- a/internal/util-complete/src/test/scala/ParserTest.scala +++ b/internal/util-complete/src/test/scala/ParserTest.scala @@ -56,27 +56,29 @@ object ParserTest extends Properties("Completing Parser") { def checkOne(in: String, parser: Parser[_], expect: Completion): Prop = completions(parser, in, 1) == Completions.single(expect) - def checkAll(in: String, parser: Parser[_], expect: Completions): Prop = - { - val cs = completions(parser, in, 1) - ("completions: " + cs) |: ("Expected: " + expect) |: (cs == expect: Prop) - } + def checkAll(in: String, parser: Parser[_], expect: Completions): Prop = { + val cs = completions(parser, in, 1) + ("completions: " + cs) |: ("Expected: " + expect) |: (cs == expect: Prop) + } def checkInvalid(in: String) = (("token '" + in + "'") |: checkInv(in, nested)) && (("display '" + in + "'") |: checkInv(in, nestedDisplay)) - def checkInv(in: String, parser: Parser[_]): Prop = - { - val cs = completions(parser, in, 1) - ("completions: " + cs) |: (cs == Completions.nil: Prop) - } + def checkInv(in: String, parser: Parser[_]): Prop = { + val cs = completions(parser, in, 1) + ("completions: " + cs) |: (cs == Completions.nil: Prop) + } - property("nested tokens a") = checkSingle("", Completion.token("", "a1"))(Completion.displayOnly("")) - property("nested tokens a1") = checkSingle("a", Completion.token("a", "1"))(Completion.displayOnly("")) + property("nested tokens a") = + checkSingle("", Completion.token("", "a1"))(Completion.displayOnly("")) + property("nested tokens a1") = + checkSingle("a", Completion.token("a", "1"))(Completion.displayOnly("")) property("nested tokens a inv") = checkInvalid("b") - property("nested tokens b") = checkSingle("a1", Completion.token("", "b2"))(Completion.displayOnly("")) - property("nested tokens b2") = checkSingle("a1b", Completion.token("b", "2"))(Completion.displayOnly("")) + property("nested tokens b") = + checkSingle("a1", Completion.token("", "b2"))(Completion.displayOnly("")) + property("nested tokens b2") = + checkSingle("a1b", Completion.token("b", "2"))(Completion.displayOnly("")) property("nested tokens b inv") = checkInvalid("a1a") property("nested tokens c") = checkSingle("a1b2", Completion.suggestion("c3"))() property("nested tokens c3") = checkSingle("a1b2c", Completion.suggestion("3"))() @@ -86,14 +88,16 @@ object ParserTest extends Properties("Completing Parser") { property("suggest port") = checkOne(" ", spacePort, Completion.displayOnly("")) property("no suggest at end") = checkOne("asdf", "asdf", Completion.suggestion("")) property("no suggest at token end") = checkOne("asdf", token("asdf"), Completion.suggestion("")) - property("empty suggest for examples") = checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestion("")) - property("empty suggest for examples token") = checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestion("")) + property("empty suggest for examples") = + checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestion("")) + property("empty suggest for examples token") = + checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestion("")) val colors = Set("blue", "green", "red") val base = (seen: Seq[String]) => token(ID examples (colors -- seen)) val sep = token(Space) val repeat = repeatDep(base, sep) - def completionStrings(ss: Set[String]): Completions = Completions(ss.map { s => Completion.token("", s) }) + def completionStrings(ss: Set[String]) = Completions(ss map (Completion.token("", _))) property("repeatDep no suggestions for bad input") = checkInv(".", repeat) property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors)) @@ -116,7 +120,9 @@ object ParserExample { val name = token("test") val options = (ws ~> token("quick" | "failed" | "new")).* val exampleSet = Set("am", "is", "are", "was", "were") - val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false))).* + val include = (ws ~> token( + examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false) + )).* val t = name ~ options ~ include diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index 9cb416840..30a633459 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -8,31 +8,31 @@ class FileExamplesTest extends UnitSpec { "listing all files in an absolute base directory" should "produce the entire base directory's contents" in { - val _ = new DirectoryStructure { - fileExamples().toList should contain theSameElementsAs (allRelativizedPaths) - } + val _ = new DirectoryStructure { + fileExamples().toList should contain theSameElementsAs (allRelativizedPaths) } + } "listing files with a prefix that matches none" should "produce an empty list" in { - val _ = new DirectoryStructure(withCompletionPrefix = "z") { - fileExamples().toList shouldBe empty - } + val _ = new DirectoryStructure(withCompletionPrefix = "z") { + fileExamples().toList shouldBe empty } + } "listing single-character prefixed files" should "produce matching paths only" in { - val _ = new DirectoryStructure(withCompletionPrefix = "f") { - fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) - } + val _ = new DirectoryStructure(withCompletionPrefix = "f") { + fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) } + } "listing directory-prefixed files" should "produce matching paths only" in { - val _ = new DirectoryStructure(withCompletionPrefix = "far") { - fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) - } + val _ = new DirectoryStructure(withCompletionPrefix = "far") { + fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) } + } it should "produce sub-dir contents only when appending a file separator to the directory" in { val _ = new DirectoryStructure(withCompletionPrefix = "far" + File.separator) { @@ -42,17 +42,17 @@ class FileExamplesTest extends UnitSpec { "listing files with a sub-path prefix" should "produce matching paths only" in { - val _ = new DirectoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { - fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) - } + val _ = new DirectoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { + fileExamples().toList should contain theSameElementsAs (prefixedPathsOnly) } + } "completing a full path" should "produce a list with an empty string" in { - val _ = new DirectoryStructure(withCompletionPrefix = "bazaar") { - fileExamples().toList shouldEqual List("") - } + val _ = new DirectoryStructure(withCompletionPrefix = "bazaar") { + fileExamples().toList shouldEqual List("") } + } // TODO: Remove DelayedInit - https://github.com/scala/scala/releases/tag/v2.11.0-RC1 class DirectoryStructure(withCompletionPrefix: String = "") extends DelayedInit { @@ -64,17 +64,19 @@ class FileExamplesTest extends UnitSpec { var nestedDirectories: List[File] = _ def allRelativizedPaths: List[String] = - (childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories).map(relativize(baseDir, _).get) + (childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories) + .map(relativize(baseDir, _).get) def prefixedPathsOnly: List[String] = - allRelativizedPaths.filter(_ startsWith withCompletionPrefix).map(_ substring withCompletionPrefix.length) + allRelativizedPaths + .filter(_ startsWith withCompletionPrefix) + .map(_ substring withCompletionPrefix.length) override def delayedInit(testBody: => Unit): Unit = { - withTemporaryDirectory { - tempDir => - createSampleDirStructure(tempDir) - fileExamples = new FileExamples(baseDir, withCompletionPrefix) - testBody + withTemporaryDirectory { tempDir => + createSampleDirStructure(tempDir) + fileExamples = new FileExamples(baseDir, withCompletionPrefix) + testBody } } @@ -90,7 +92,8 @@ class FileExamplesTest extends UnitSpec { baseDir = tempDir } - private def toChildFiles(baseDir: File, files: List[String]): List[File] = files.map(new File(baseDir, _)) + private def toChildFiles(baseDir: File, files: List[String]): List[File] = + files.map(new File(baseDir, _)) } } diff --git a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala index b043497db..b1149fe88 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala @@ -5,7 +5,8 @@ class FixedSetExamplesTest extends UnitSpec { "adding a prefix" should "produce a smaller set of examples with the prefix removed" in { val _ = new Examples { - fixedSetExamples.withAddedPrefix("f")() should contain theSameElementsAs (List("oo", "ool", "u")) + fixedSetExamples.withAddedPrefix("f")() should contain theSameElementsAs + (List("oo", "ool", "u")) fixedSetExamples.withAddedPrefix("fo")() should contain theSameElementsAs (List("o", "ol")) fixedSetExamples.withAddedPrefix("b")() should contain theSameElementsAs (List("ar")) } diff --git a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala index 17891be4f..93486dd54 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -7,62 +7,70 @@ class ParserWithExamplesTest extends UnitSpec { "listing a limited number of completions" should "grab only the needed number of elements from the iterable source of examples" in { - val _ = new ParserWithLazyExamples { - parserWithExamples.completions(0) - examples.size shouldEqual maxNumberOfExamples - } + val _ = new ParserWithLazyExamples { + parserWithExamples.completions(0) + examples.size shouldEqual maxNumberOfExamples } + } "listing only valid completions" should "use the delegate parser to remove invalid examples" in { - val _ = new ParserWithValidExamples { - val validCompletions = Completions(Set( + val _ = new ParserWithValidExamples { + val validCompletions = Completions( + Set( suggestion("blue"), suggestion("red") )) - parserWithExamples.completions(0) shouldEqual validCompletions - } + parserWithExamples.completions(0) shouldEqual validCompletions } + } "listing valid completions in a derived parser" should "produce only valid examples that start with the character of the derivation" in { - val _ = new ParserWithValidExamples { - val derivedCompletions = Completions(Set( + val _ = new ParserWithValidExamples { + val derivedCompletions = Completions( + Set( suggestion("lue") )) - parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions - } + parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions } + } "listing valid and invalid completions" should "produce the entire source of examples" in { - val _ = new parserWithAllExamples { - val completions = Completions(examples.map(suggestion(_)).toSet) - parserWithExamples.completions(0) shouldEqual completions - } + val _ = new parserWithAllExamples { + val completions = Completions(examples.map(suggestion(_)).toSet) + parserWithExamples.completions(0) shouldEqual completions } + } "listing valid and invalid completions in a derived parser" should "produce only examples that start with the character of the derivation" in { - val _ = new parserWithAllExamples { - val derivedCompletions = Completions(Set( + val _ = new parserWithAllExamples { + val derivedCompletions = Completions( + Set( suggestion("lue"), suggestion("lock") )) - parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions - } + parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions } + } - class ParserWithLazyExamples extends ParserExample(GrowableSourceOfExamples(), maxNumberOfExamples = 5, removeInvalidExamples = false) + class ParserWithLazyExamples + extends ParserExample( + GrowableSourceOfExamples(), + maxNumberOfExamples = 5, + removeInvalidExamples = false + ) class ParserWithValidExamples extends ParserExample(removeInvalidExamples = true) class parserWithAllExamples extends ParserExample(removeInvalidExamples = false) case class ParserExample( - examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), - maxNumberOfExamples: Int = 25, - removeInvalidExamples: Boolean + examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"), + maxNumberOfExamples: Int = 25, + removeInvalidExamples: Boolean ) { import DefaultParsers._ diff --git a/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala b/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala index 0e15fadf2..f273d795e 100644 --- a/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala +++ b/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala @@ -5,7 +5,8 @@ import scala.annotation.tailrec import Formula.{ And, True } /* -Defines a propositional logic with negation as failure and only allows stratified rule sets (negation must be acyclic) in order to have a unique minimal model. +Defines a propositional logic with negation as failure and only allows stratified rule sets +(negation must be acyclic) in order to have a unique minimal model. For example, this is not allowed: + p :- not q @@ -24,7 +25,7 @@ as is this: + https://en.wikipedia.org/wiki/Propositional_logic + https://en.wikipedia.org/wiki/Stable_model_semantics + http://www.w3.org/2005/rules/wg/wiki/negation -*/ + */ /** Disjunction (or) of the list of clauses. */ final case class Clauses(clauses: List[Clause]) { @@ -36,16 +37,21 @@ final case class Clause(body: Formula, head: Set[Atom]) /** A literal is an [[Atom]] or its [[negation|Negated]]. */ sealed abstract class Literal extends Formula { + /** The underlying (positive) atom. */ def atom: Atom + /** Negates this literal.*/ def unary_! : Literal + } + /** A variable with name `label`. */ final case class Atom(label: String) extends Literal { def atom = this def unary_! : Negated = Negated(this) } + /** * A negated atom, in the sense of negation as failure, not logical negation. * That is, it is true if `atom` is not known/defined. @@ -60,6 +66,7 @@ final case class Negated(atom: Atom) extends Literal { * This is less convenient when defining clauses, but is not less powerful.) */ sealed abstract class Formula { + /** Constructs a clause that proves `atoms` when this formula is true. */ def proves(atom: Atom, atoms: Atom*): Clause = Clause(this, (atom +: atoms).toSet) @@ -72,40 +79,46 @@ sealed abstract class Formula { case (a: Literal, And(bs)) => And(bs + a) case (a: Literal, b: Literal) => And(Set(a, b)) } + } object Formula { + /** A conjunction of literals. */ final case class And(literals: Set[Literal]) extends Formula { assert(literals.nonEmpty, "'And' requires at least one literal.") } + final case object True extends Formula + } object Logic { - def reduceAll(clauses: List[Clause], initialFacts: Set[Literal]): Either[LogicException, Matched] = + def reduceAll( + clauses: List[Clause], + initialFacts: Set[Literal] + ): Either[LogicException, Matched] = reduce(Clauses(clauses), initialFacts) /** - * Computes the variables in the unique stable model for the program represented by `clauses` and `initialFacts`. - * `clause` may not have any negative feedback (that is, negation is acyclic) + * Computes the variables in the unique stable model for the program represented by `clauses` and + * `initialFacts`. `clause` may not have any negative feedback (that is, negation is acyclic) * and `initialFacts` cannot be in the head of any clauses in `clause`. * These restrictions ensure that the logic program has a unique minimal model. */ - def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = - { - val (posSeq, negSeq) = separate(initialFacts.toSeq) - val (pos, neg) = (posSeq.toSet, negSeq.toSet) + def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = { + val (posSeq, negSeq) = separate(initialFacts.toSeq) + val (pos, neg) = (posSeq.toSet, negSeq.toSet) - val problem = - checkContradictions(pos, neg) orElse - checkOverlap(clauses, pos) orElse - checkAcyclic(clauses) + val problem = + checkContradictions(pos, neg) orElse + checkOverlap(clauses, pos) orElse + checkAcyclic(clauses) - problem.toLeft( - reduce0(clauses, initialFacts, Matched.empty) - ) - } + problem.toLeft( + reduce0(clauses, initialFacts, Matched.empty) + ) + } /** * Verifies `initialFacts` are not in the head of any `clauses`. @@ -113,13 +126,19 @@ object Logic { * This isn't necessarily a problem, but the main sbt use cases expects * a proven atom to have at least one clause satisfied. */ - private[this] def checkOverlap(clauses: Clauses, initialFacts: Set[Atom]): Option[InitialOverlap] = { + private[this] def checkOverlap( + clauses: Clauses, + initialFacts: Set[Atom] + ): Option[InitialOverlap] = { val as = atoms(clauses) val initialOverlap = initialFacts.filter(as.inHead) if (initialOverlap.nonEmpty) Some(new InitialOverlap(initialOverlap)) else None } - private[this] def checkContradictions(pos: Set[Atom], neg: Set[Atom]): Option[InitialContradictions] = { + private[this] def checkContradictions( + pos: Set[Atom], + neg: Set[Atom] + ): Option[InitialContradictions] = { val contradictions = pos intersect neg if (contradictions.nonEmpty) Some(new InitialContradictions(contradictions)) else None } @@ -129,14 +148,17 @@ object Logic { val cycle = Dag.findNegativeCycle(graph(deps)) if (cycle.nonEmpty) Some(new CyclicNegation(cycle)) else None } + private[this] def graph(deps: Map[Atom, Set[Literal]]) = new Dag.DirectedSignedGraph[Atom] { type Arrow = Literal def nodes = deps.keys.toList def dependencies(a: Atom) = deps.getOrElse(a, Set.empty).toList + def isNegative(b: Literal) = b match { case Negated(_) => true case Atom(_) => false } + def head(b: Literal) = b.atom } @@ -144,52 +166,72 @@ object Logic { (Map.empty[Atom, Set[Literal]] /: clauses.clauses) { case (m, Clause(formula, heads)) => val deps = literals(formula) - (m /: heads) { (n, head) => n.updated(head, n.getOrElse(head, Set.empty) ++ deps) } + (m /: heads) { (n, head) => + n.updated(head, n.getOrElse(head, Set.empty) ++ deps) + } } sealed abstract class LogicException(override val toString: String) - final class InitialContradictions(val literals: Set[Atom]) extends LogicException("Initial facts cannot be both true and false:\n\t" + literals.mkString("\n\t")) - final class InitialOverlap(val literals: Set[Atom]) extends LogicException("Initial positive facts cannot be implied by any clauses:\n\t" + literals.mkString("\n\t")) - final class CyclicNegation(val cycle: List[Literal]) extends LogicException("Negation may not be involved in a cycle:\n\t" + cycle.mkString("\n\t")) + + final class InitialContradictions(val literals: Set[Atom]) + extends LogicException( + "Initial facts cannot be both true and false:\n\t" + literals.mkString("\n\t") + ) + + final class InitialOverlap(val literals: Set[Atom]) + extends LogicException( + "Initial positive facts cannot be implied by any clauses:\n\t" + literals.mkString("\n\t") + ) + + final class CyclicNegation(val cycle: List[Literal]) + extends LogicException( + "Negation may not be involved in a cycle:\n\t" + cycle.mkString("\n\t") + ) /** Tracks proven atoms in the reverse order they were proved. */ final class Matched private (val provenSet: Set[Atom], reverseOrdered: List[Atom]) { def add(atoms: Set[Atom]): Matched = add(atoms.toList) + def add(atoms: List[Atom]): Matched = { val newOnly = atoms.filterNot(provenSet) new Matched(provenSet ++ newOnly, newOnly ::: reverseOrdered) } + def ordered: List[Atom] = reverseOrdered.reverse override def toString = ordered.map(_.label).mkString("Matched(", ",", ")") } + object Matched { val empty = new Matched(Set.empty, Nil) } /** Separates a sequence of literals into `(pos, neg)` atom sequences. */ - private[this] def separate(lits: Seq[Literal]): (Seq[Atom], Seq[Atom]) = Util.separate(lits) { - case a: Atom => Left(a) - case Negated(n) => Right(n) - } + private[this] def separate(lits: Seq[Literal]): (Seq[Atom], Seq[Atom]) = + Util.separate(lits) { + case a: Atom => Left(a) + case Negated(n) => Right(n) + } /** * Finds clauses that have no body and thus prove their head. * Returns `(, )`. */ - private[this] def findProven(c: Clauses): (Set[Atom], List[Clause]) = - { - val (proven, unproven) = c.clauses.partition(_.body == True) - (proven.flatMap(_.head).toSet, unproven) - } + private[this] def findProven(c: Clauses): (Set[Atom], List[Clause]) = { + val (proven, unproven) = c.clauses.partition(_.body == True) + (proven.flatMap(_.head).toSet, unproven) + } + private[this] def keepPositive(lits: Set[Literal]): Set[Atom] = lits.collect { case a: Atom => a }.toSet // precondition: factsToProcess contains no contradictions - @tailrec - private[this] def reduce0(clauses: Clauses, factsToProcess: Set[Literal], state: Matched): Matched = + @tailrec private[this] def reduce0( + clauses: Clauses, + factsToProcess: Set[Literal], + state: Matched + ): Matched = applyAll(clauses, factsToProcess) match { - case None => // all of the remaining clauses failed on the new facts - state + case None => state // all of the remaining clauses failed on the new facts case Some(applied) => val (proven, unprovenClauses) = findProven(applied) val processedFacts = state add keepPositive(factsToProcess) @@ -199,7 +241,8 @@ object Logic { newState // no remaining clauses, done. else { val unproven = Clauses(unprovenClauses) - val nextFacts: Set[Literal] = if (newlyProven.nonEmpty) newlyProven.toSet else inferFailure(unproven) + val nextFacts: Set[Literal] = + if (newlyProven.nonEmpty) newlyProven.toSet else inferFailure(unproven) reduce0(unproven, nextFacts, newState) } } @@ -208,26 +251,30 @@ object Logic { * Finds negated atoms under the negation as failure rule and returns them. * This should be called only after there are no more known atoms to be substituted. */ - private[this] def inferFailure(clauses: Clauses): Set[Literal] = - { - /* At this point, there is at least one clause and one of the following is the case as the result of the acyclic negation rule: - i. there is at least one variable that occurs in a clause body but not in the head of a clause - ii. there is at least one variable that occurs in the head of a clause and does not transitively depend on a negated variable - In either case, each such variable x cannot be proven true and therefore proves 'not x' (negation as failure, !x in the code). - */ - val allAtoms = atoms(clauses) - val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse) - if (newFacts.nonEmpty) - newFacts - else { - val possiblyTrue = hasNegatedDependency(clauses.clauses, Relation.empty, Relation.empty) - val newlyFalse: Set[Literal] = negated(allAtoms.inHead -- possiblyTrue) - if (newlyFalse.nonEmpty) - newlyFalse - else // should never happen due to the acyclic negation rule - sys.error(s"No progress:\n\tclauses: $clauses\n\tpossibly true: $possiblyTrue") - } + private[this] def inferFailure(clauses: Clauses): Set[Literal] = { + /* At this point, there is at least one clause and one of the following is the case as the + result of the acyclic negation rule: + i. there is at least one variable that occurs in a clause body but not in the head of a + clause + ii. there is at least one variable that occurs in the head of a clause and does not + transitively depend on a negated variable + + In either case, each such variable x cannot be proven true and therefore proves 'not x' + (negation as failure, !x in the code). + */ + val allAtoms = atoms(clauses) + val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse) + if (newFacts.nonEmpty) + newFacts + else { + val possiblyTrue = hasNegatedDependency(clauses.clauses, Relation.empty, Relation.empty) + val newlyFalse: Set[Literal] = negated(allAtoms.inHead -- possiblyTrue) + if (newlyFalse.nonEmpty) + newlyFalse + else // should never happen due to the acyclic negation rule + sys.error(s"No progress:\n\tclauses: $clauses\n\tpossibly true: $possiblyTrue") } + } private[this] def negated(atoms: Set[Atom]): Set[Literal] = atoms.map(a => Negated(a)) @@ -238,11 +285,16 @@ object Logic { * d :- a */ @tailrec - def hasNegatedDependency(clauses: Seq[Clause], posDeps: Relation[Atom, Atom], negDeps: Relation[Atom, Atom]): List[Atom] = + def hasNegatedDependency( + clauses: Seq[Clause], + posDeps: Relation[Atom, Atom], + negDeps: Relation[Atom, Atom] + ): List[Atom] = clauses match { case Seq() => // because cycles between positive literals are allowed, this isn't strictly a topological sort Dag.topologicalSortUnchecked(negDeps._1s)(posDeps.reverse) + case Clause(formula, head) +: tail => // collect direct positive and negative literals and track them in separate graphs val (pos, neg) = directDeps(formula) @@ -259,6 +311,7 @@ object Logic { case Negated(a) => Right(a) case a: Atom => Left(a) } + private[this] def literals(formula: Formula): Set[Literal] = formula match { case And(lits) => lits case l: Literal => Set(l) @@ -278,10 +331,13 @@ object Logic { /** Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses. */ final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]) { + /** Concatenates this with `as`. */ def ++(as: Atoms): Atoms = Atoms(inHead ++ as.inHead, inFormula ++ as.inFormula) + /** Atoms that cannot be true because they do not occur in a head. */ def triviallyFalse: Set[Atom] = inFormula -- inHead + } /** @@ -297,29 +353,26 @@ object Logic { * Postcondition: no atom in `facts` is present in the result * Postcondition: No clauses have an empty head */ - def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = - { - val newClauses = - if (facts.isEmpty) - cs.clauses.filter(_.head.nonEmpty) // still need to drop clauses with an empty head - else - cs.clauses.map(c => applyAll(c, facts)).flatMap(_.toList) - if (newClauses.isEmpty) None else Some(Clauses(newClauses)) - } - - def applyAll(c: Clause, facts: Set[Literal]): Option[Clause] = - { - val atoms = facts.map(_.atom) - val newHead = c.head -- atoms // 3. - if (newHead.isEmpty) // 4. empty head - None + def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = { + val newClauses = + if (facts.isEmpty) + cs.clauses.filter(_.head.nonEmpty) // still need to drop clauses with an empty head else - substitute(c.body, facts).map(f => Clause(f, newHead)) // 1, 2 - } + cs.clauses.map(c => applyAll(c, facts)).flatMap(_.toList) + if (newClauses.isEmpty) None else Some(Clauses(newClauses)) + } + + def applyAll(c: Clause, facts: Set[Literal]): Option[Clause] = { + val atoms = facts.map(_.atom) + val newHead = c.head -- atoms // 3. + if (newHead.isEmpty) // 4. empty head + None + else + substitute(c.body, facts).map(f => Clause(f, newHead)) // 1, 2 + } /** Derives the formula that results from substituting `facts` into `formula`. */ - @tailrec - def substitute(formula: Formula, facts: Set[Literal]): Option[Formula] = formula match { + @tailrec def substitute(formula: Formula, facts: Set[Literal]): Option[Formula] = formula match { case And(lits) => def negated(lits: Set[Literal]): Set[Literal] = lits.map(a => !a) if (lits.exists(negated(facts))) // 2.