From cf053034f71e63a263cfbbaae790e4a0a6dd369b Mon Sep 17 00:00:00 2001 From: William Harvey Date: Tue, 3 May 2022 21:45:03 -0400 Subject: [PATCH] Remove CliConfig from ZIO environment in tab completion code (#131) --- .../scala/zio/cli/completion/Completion.scala | 7 +- .../zio/cli/completion/RegularLanguage.scala | 63 ++++++------ .../cli/completion/RegularLanguageSpec.scala | 97 +++++++++---------- 3 files changed, 79 insertions(+), 88 deletions(-) diff --git a/zio-cli/shared/src/main/scala/zio/cli/completion/Completion.scala b/zio-cli/shared/src/main/scala/zio/cli/completion/Completion.scala index 451274ae..a0dbaa55 100644 --- a/zio-cli/shared/src/main/scala/zio/cli/completion/Completion.scala +++ b/zio-cli/shared/src/main/scala/zio/cli/completion/Completion.scala @@ -43,11 +43,8 @@ object Completion { * Repeatedly differentiate the language w.r.t. each of the tokens that * occur before the cursor. */ - val derivative: UIO[RegularLanguage] = ZIO.foldLeft(unclustered)(language)((lang, word) => - lang - .derive(word) - .provideEnvironment(ZEnvironment(cliConfig)) - ) + val derivative: UIO[RegularLanguage] = + ZIO.foldLeft(unclustered)(language)((lang, word) => lang.derive(word, cliConfig)) val wordToComplete = if (index < words.size) words(index) else "" diff --git a/zio-cli/shared/src/main/scala/zio/cli/completion/RegularLanguage.scala b/zio-cli/shared/src/main/scala/zio/cli/completion/RegularLanguage.scala index c4f002e7..e20f1840 100644 --- a/zio-cli/shared/src/main/scala/zio/cli/completion/RegularLanguage.scala +++ b/zio-cli/shared/src/main/scala/zio/cli/completion/RegularLanguage.scala @@ -31,7 +31,7 @@ sealed trait RegularLanguage extends Product with Serializable { * @param token The string to use for calculation of the Brzozowski derivative. * @return Brzozowski derivative wrapped in an UIO instance. */ - def derive(token: String): URIO[CliConfig, RegularLanguage] + def derive(token: String, cliConfig: CliConfig): UIO[RegularLanguage] def ~(other: RegularLanguage): RegularLanguage = Cat(this, other) def ~(other: String): RegularLanguage = Cat(this, StringToken(other)) @@ -47,10 +47,10 @@ sealed trait RegularLanguage extends Product with Serializable { * @param tokens * @return true if and only if `tokens` is in the language. */ - def contains(tokens: List[String]): URIO[CliConfig, Boolean] = { - val derivative = ZIO.foldLeft(tokens)(this)((language, word) => language.derive(word)) - derivative.map(dx => dx.isNullable) - } + def contains(tokens: List[String], cliConfig: CliConfig): UIO[Boolean] = + ZIO + .foldLeft(tokens)(this)((language, word) => language.derive(word, cliConfig)) + .map(dx => dx.isNullable) /** * Returns a set consisting of the first token of all strings in this language @@ -92,7 +92,7 @@ object RegularLanguage { case object Empty extends RegularLanguage { def isNullable: Boolean = false - def derive(token: String) = ZIO.succeed(Empty) + def derive(token: String, cliConfig: CliConfig) = ZIO.succeed(Empty) def firstTokens(prefix: String, compgen: Compgen): UIO[Set[String]] = ZIO.succeed(Set.empty) @@ -106,7 +106,7 @@ object RegularLanguage { case object Epsilon extends RegularLanguage { def isNullable: Boolean = true - def derive(token: String) = ZIO.succeed(Empty) + def derive(token: String, cliConfig: CliConfig) = ZIO.succeed(Empty) def firstTokens(prefix: String, compgen: Compgen): UIO[Set[String]] = ZIO.succeed(Set("")) @@ -123,15 +123,13 @@ object RegularLanguage { * contains only `value`. */ final case class StringToken(value: String) extends Token { - def derive(token: String) = - CliConfig.cliConfig - .map(cliConfig => - if (cliConfig.caseSensitive) - value.equals(token) - else - value.equalsIgnoreCase(token) - ) - .map(isMatch => if (isMatch) Epsilon else Empty) + def derive(token: String, cliConfig: CliConfig) = { + val isMatch = if (cliConfig.caseSensitive) value.equals(token) else value.equalsIgnoreCase(token) + isMatch match { + case true => ZIO.succeed(Epsilon) + case false => ZIO.succeed(Empty) + } + } def firstTokens(prefix: String, compgen: Compgen): UIO[Set[String]] = ZIO.succeed(Set(value + " ").filter(_.startsWith(prefix))) @@ -143,7 +141,7 @@ object RegularLanguage { * be aliased or renamed to be different than `Command.Single.name`.) */ case object AnyStringToken extends Token { - def derive(token: String) = ZIO.succeed(Epsilon) + def derive(token: String, cliConfig: CliConfig) = ZIO.succeed(Epsilon) def firstTokens(prefix: String, compgen: Compgen): UIO[Set[String]] = ZIO.succeed(Set.empty) @@ -154,15 +152,12 @@ object RegularLanguage { * any strings `s` where `value.validate(s)` succeeds. */ final case class PrimTypeToken(value: PrimType[Any]) extends Token { - def derive(token: String) = - CliConfig.cliConfig - .flatMap(cliConfig => - value - .validate(token, cliConfig) - .fold( - _ => Empty, - _ => Epsilon - ) + def derive(token: String, cliConfig: CliConfig) = + value + .validate(token, cliConfig) + .fold( + _ => Empty, + _ => Epsilon ) def firstTokens(prefix: String, compgen: Compgen): UIO[Set[String]] = @@ -175,13 +170,13 @@ object RegularLanguage { final case class Cat(left: RegularLanguage, right: RegularLanguage) extends RegularLanguage { lazy val isNullable: Boolean = left.isNullable && right.isNullable - def derive(token: String) = + def derive(token: String, cliConfig: CliConfig) = if (left.isNullable) - left.derive(token).zip(right.derive(token)).map { case (dx, dy) => + left.derive(token, cliConfig).zip(right.derive(token, cliConfig)).map { case (dx, dy) => (dx ~ right) | dy } else - left.derive(token).map(dx => dx ~ right) + left.derive(token, cliConfig).map(dx => dx ~ right) def firstTokens(prefix: String, compgen: Compgen): UIO[Set[String]] = if (left.isNullable) @@ -200,8 +195,8 @@ object RegularLanguage { final case class Alt(left: RegularLanguage, right: RegularLanguage) extends RegularLanguage { lazy val isNullable: Boolean = left.isNullable || right.isNullable - def derive(token: String) = - left.derive(token).zip(right.derive(token)).map { case (dx, dy) => dx | dy } + def derive(token: String, cliConfig: CliConfig) = + left.derive(token, cliConfig).zip(right.derive(token, cliConfig)).map { case (dx, dy) => dx | dy } def firstTokens(prefix: String, compgen: Compgen): UIO[Set[String]] = left.firstTokens(prefix, compgen).zip(right.firstTokens(prefix, compgen)).map { case (left, right) => @@ -217,12 +212,12 @@ object RegularLanguage { final case class Rep(language: RegularLanguage, min: Option[Int], max: Option[Int]) extends RegularLanguage { def isNullable: Boolean = min.forall(_ <= 0) - def derive(token: String) = { + def derive(token: String, cliConfig: CliConfig) = { val newMin = min.map(_ - 1).filter(_ > 0) val newMax = max.map(_ - 1) if (newMax.forall(_ >= 0)) - language.derive(token).map(dx => dx ~ language.rep(newMin, newMax)) + language.derive(token, cliConfig).map(dx => dx ~ language.rep(newMin, newMax)) else ZIO.succeed(Empty) } @@ -251,7 +246,7 @@ object RegularLanguage { lazy val desugared: RegularLanguage = values.foldLeft[RegularLanguage](Epsilon)((agg, lang) => agg | (lang ~ Permutation(values.filter(_ != lang): _*))) - def derive(token: String) = desugared.derive(token) + def derive(token: String, cliConfig: CliConfig) = desugared.derive(token, cliConfig) def firstTokens(prefix: String, compgen: Compgen): UIO[Set[String]] = ZIO.foreach(values)(lang => lang.firstTokens(prefix, compgen)).map(_.toSet.flatten) diff --git a/zio-cli/shared/src/test/scala/zio/cli/completion/RegularLanguageSpec.scala b/zio-cli/shared/src/test/scala/zio/cli/completion/RegularLanguageSpec.scala index af84662d..fe34fa8b 100644 --- a/zio-cli/shared/src/test/scala/zio/cli/completion/RegularLanguageSpec.scala +++ b/zio-cli/shared/src/test/scala/zio/cli/completion/RegularLanguageSpec.scala @@ -1,6 +1,5 @@ package zio.cli.completion -import zio._ import zio.test.Assertion._ import zio.test._ import zio.cli._ @@ -12,36 +11,36 @@ object RegularLanguageSpec extends ZIOSpecDefault { test("Empty language rejects all strings")( check(Gen.listOfBounded(0, 5)(Gen.alphaNumericString))(tokens => assertZIO( - RegularLanguage.Empty.contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.Empty.contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) ), suite("Epsilon language")( test("Epsilon language accepts the empty string")( assertZIO( - RegularLanguage.Epsilon.contains(List.empty) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.Epsilon.contains(List.empty, CliConfig.default) + )(equalTo(true)) ), test("Epsilon language rejects all nonempty strings")( check(Gen.listOfBounded(1, 5)(Gen.alphaNumericString))(tokens => assertZIO( - RegularLanguage.Empty.contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.Empty.contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) ), suite("StringToken language")( test("StringToken language accepts its target string")( assertZIO( - RegularLanguage.StringToken("foo").contains(List("foo")) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.StringToken("foo").contains(List("foo"), CliConfig.default) + )(equalTo(true)) ), test("StringToken language rejects anything other than its target string")( check(Gen.alphaNumericString.filter(_ != "foo"))(token => assertZIO( - RegularLanguage.StringToken("foo").contains(List(token)) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.StringToken("foo").contains(List(token), CliConfig.default) + )(equalTo(false)) ) ) ), @@ -50,8 +49,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { test("PrimType.Bool language accepts values that correspond to 'true' and 'false' ")( check(Gen.fromIterable(PrimType.Bool.TrueValues ++ PrimType.Bool.FalseValues))(token => assertZIO( - RegularLanguage.PrimTypeToken(PrimType.Bool(None)).contains(List(token)) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.PrimTypeToken(PrimType.Bool(None)).contains(List(token), CliConfig.default) + )(equalTo(true)) ) ), test("PrimType.Bool language rejects values that do not correspond to 'true'/'false'")( @@ -61,8 +60,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { .filter(s => !PrimType.Bool.TrueValues(s) && !PrimType.Bool.FalseValues(s)) )(token => assertZIO( - RegularLanguage.PrimTypeToken(PrimType.Bool(None)).contains(List(token)) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.PrimTypeToken(PrimType.Bool(None)).contains(List(token), CliConfig.default) + )(equalTo(false)) ) ) ), @@ -87,8 +86,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { ) )(token => assertZIO( - RegularLanguage.PrimTypeToken(PrimType.ZoneId).contains(List(token)) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.PrimTypeToken(PrimType.ZoneId).contains(List(token), CliConfig.default) + )(equalTo(true)) ) ), test("PrimType.Bool language rejects some values that are not valid time zone IDs")( @@ -96,8 +95,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { Gen.fromIterable(Iterable("invalid", "whatever", "foo")) )(token => assertZIO( - RegularLanguage.PrimTypeToken(PrimType.ZoneId).contains(List(token)) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + RegularLanguage.PrimTypeToken(PrimType.ZoneId).contains(List(token), CliConfig.default) + )(equalTo(false)) ) ) ) @@ -107,8 +106,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { test("Accepts 'foo' 'bar' 'baz'")( assertZIO( ("foo" ~ "bar" ~ "baz") - .contains(List("foo", "bar", "baz")) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(List("foo", "bar", "baz"), CliConfig.default) + )(equalTo(true)) ), test("Rejects everything that is not 'foo' 'bar' 'baz'")( check( @@ -124,8 +123,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { )(tokens => assertZIO( ("foo" ~ "bar" ~ "baz") - .contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) ) @@ -135,14 +134,14 @@ object RegularLanguageSpec extends ZIOSpecDefault { test("Accepts 'foo' 'bar'")( assertZIO( ("foo" ~ "bar" | "foo" ~ "baz") - .contains(List("foo", "bar")) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(List("foo", "bar"), CliConfig.default) + )(equalTo(true)) ), test("Accepts 'foo' 'baz'")( assertZIO( ("foo" ~ "bar" | "foo" ~ "baz") - .contains(List("foo", "baz")) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(List("foo", "baz"), CliConfig.default) + )(equalTo(true)) ), test("Rejects everything that is not 'foo' 'bar' | 'foo' 'baz'")( check( @@ -157,8 +156,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { )(tokens => assertZIO( ("foo" ~ "bar" | "foo" ~ "baz") - .contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) ) @@ -182,8 +181,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { ) )(tokens => assertZIO( - ("foo" ~ "bar" | "foo" ~ "baz").*.contains(tokens) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + ("foo" ~ "bar" | "foo" ~ "baz").*.contains(tokens, CliConfig.default) + )(equalTo(true)) ) ), test("Rejects everything except zero or more repetitions of 'foo' 'bar' or 'foo' 'baz'")( @@ -200,8 +199,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { ) )(tokens => assertZIO( - ("foo" ~ "bar" | "foo" ~ "baz").*.contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + ("foo" ~ "bar" | "foo" ~ "baz").*.contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) ), @@ -220,8 +219,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { ) )(tokens => assertZIO( - ("foo" ~ "bar" | "foo" ~ "baz").rep(Some(2), Some(4)).contains(tokens) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + ("foo" ~ "bar" | "foo" ~ "baz").rep(Some(2), Some(4)).contains(tokens, CliConfig.default) + )(equalTo(true)) ) ), test("Rejects everything except two to four or more repetitions of 'foo' 'bar' or 'foo' 'baz'")( @@ -242,8 +241,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { ) )(tokens => assertZIO( - ("foo" ~ "bar" | "foo" ~ "baz").rep(Some(2), Some(4)).contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + ("foo" ~ "bar" | "foo" ~ "baz").rep(Some(2), Some(4)).contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) ) @@ -259,8 +258,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { .Permutation( List("a", "b", "c", "d").map(RegularLanguage.StringToken(_)): _* ) - .contains(tokens) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(tokens, CliConfig.default) + )(equalTo(true)) ) ), test("Rejects everything except for permutations of {'a', 'b', 'c', 'd'}")( @@ -283,8 +282,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { .Permutation( List("a", "b", "c", "d").map(RegularLanguage.StringToken(_)): _* ) - .contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) ), @@ -314,8 +313,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { "b" | "c", RegularLanguage.StringToken("d").* ) - .contains(tokens) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(tokens, CliConfig.default) + )(equalTo(true)) ) ), test("Rejects language non-members")( @@ -339,8 +338,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { "b" | "c", RegularLanguage.StringToken("d").* ) - .contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + .contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) ), @@ -370,8 +369,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { RegularLanguage.StringToken("c").?, RegularLanguage.StringToken("d").? ) ~ "z" - ).contains(tokens) - )(equalTo(true)).provideEnvironment(ZEnvironment(CliConfig.default)) + ).contains(tokens, CliConfig.default) + )(equalTo(true)) ) ), test("Rejects language non-members")( @@ -399,8 +398,8 @@ object RegularLanguageSpec extends ZIOSpecDefault { RegularLanguage.StringToken("c").?, RegularLanguage.StringToken("d").? ) ~ "z" - ).contains(tokens) - )(equalTo(false)).provideEnvironment(ZEnvironment(CliConfig.default)) + ).contains(tokens, CliConfig.default) + )(equalTo(false)) ) ) )