From 59465d9e1f8ed884ffb79b9ab8454e61d677acb9 Mon Sep 17 00:00:00 2001 From: Guillaume Poirier Date: Tue, 19 Jun 2018 15:03:14 -0400 Subject: [PATCH] Adding minimal support for commands in inspect There's also a special case for aliases that will try to resolve the target of the alias to a task key if possible and display the output of that key if found. see https://github.com/sbt/sbt/issues/2881 --- main/src/main/scala/sbt/Main.scala | 4 +- .../scala/sbt/internal/CommandStrings.scala | 5 +- .../src/main/scala/sbt/internal/Inspect.scala | 48 +++++++++++++++---- notes/1.2.0/inspect-alias.md | 7 +++ sbt/src/sbt-test/project/unified/build.sbt | 4 +- 5 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 notes/1.2.0/inspect-alias.md diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 0e592984d..49094156b 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -505,8 +505,8 @@ object BuiltinCommands { SettingCompletions.setThis(extracted, settings, arg) def inspect: Command = Command(InspectCommand, inspectBrief, inspectDetailed)(Inspect.parser) { - case (s, (option, sk)) => - s.log.info(Inspect.output(s, option, sk)) + case (s, f) => + s.log.info(f()) s } diff --git a/main/src/main/scala/sbt/internal/CommandStrings.scala b/main/src/main/scala/sbt/internal/CommandStrings.scala index d30a31f29..534945f18 100644 --- a/main/src/main/scala/sbt/internal/CommandStrings.scala +++ b/main/src/main/scala/sbt/internal/CommandStrings.scala @@ -120,10 +120,11 @@ $LastCommand "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies." ) val inspectDetailed = s""" - |$InspectCommand + |$InspectCommand [-] | | For a plain setting, the value bound to the key argument is displayed using its toString method. - | Otherwise, the type of task ("Task" or "Input task") is displayed. + | For an alias, the command bound to the alias is displayed. + | Otherwise, the type of the key ("Task" or "Input task") is displayed. | | "Dependencies" shows the settings that this setting depends on. | diff --git a/main/src/main/scala/sbt/internal/Inspect.scala b/main/src/main/scala/sbt/internal/Inspect.scala index 405da6c78..a69123340 100644 --- a/main/src/main/scala/sbt/internal/Inspect.scala +++ b/main/src/main/scala/sbt/internal/Inspect.scala @@ -27,19 +27,18 @@ object Inspect { val Uses: Mode = UsesMode val Definitions: Mode = DefinitionsMode - def parser: State => Parser[(Inspect.Mode, ScopedKey[_])] = + def parser: State => Parser[() => String] = (s: State) => - spacedModeParser(s) flatMap { - case opt @ (UsesMode | DefinitionsMode) => - allKeyParser(s).map(key => (opt, Def.ScopedKey(Global, key))) - case opt @ (DependencyTreeMode | Details(_)) => spacedKeyParser(s).map(key => (opt, key)) + spacedModeParser(s) flatMap { mode => + commandHandler(s, mode) | keyHandler(s)(mode) } - val spacedModeParser: (State => Parser[Mode]) = (s: State) => { + val spacedModeParser: State => Parser[Mode] = (_: State) => { + val default = "-" ^^^ Details(false) val actual = "actual" ^^^ Details(true) val tree = "tree" ^^^ DependencyTree val uses = "uses" ^^^ Uses val definitions = "definitions" ^^^ Definitions - token(Space ~> (tree | actual | uses | definitions)) ?? Details(false) + token(Space ~> (default | tree | actual | uses | definitions)) ?? Details(false) } def allKeyParser(s: State): Parser[AttributeKey[_]] = { @@ -51,7 +50,40 @@ object Inspect { val spacedKeyParser: State => Parser[ScopedKey[_]] = (s: State) => Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s)) - def output(s: State, option: Mode, sk: Def.ScopedKey[_]): String = { + def keyHandler(s: State): Mode => Parser[() => String] = { + case opt @ (UsesMode | DefinitionsMode) => + allKeyParser(s).map(key => () => keyOutput(s, opt, Def.ScopedKey(Global, key))) + case opt @ (DependencyTreeMode | Details(_)) => + spacedKeyParser(s).map(key => () => keyOutput(s, opt, key)) + } + + def commandHandler(s: State, mode: Mode): Parser[() => String] = { + Space ~> commandParser(s).flatMap { + case (name, cmd) => + cmd.tags.get(BasicCommands.CommandAliasKey) match { + case Some((_, aliasFor)) => + def header = s"Alias for: $aliasFor" + Parser + .parse(" " ++ aliasFor, keyHandler(s)(mode)) + .fold( + // If we can't find a task key for the alias target + // we don't display anymore information + _ => success(() => header), + success + ) + case None => + success(() => s"Command: $name") + } + } + } + + def commandParser: State => Parser[(String, Command)] = { s => + oneOf(s.definedCommands.map(cmd => cmd -> cmd.nameOption) collect { + case (cmd, Some(name)) => DefaultParsers.literal(name).map(_ -> cmd) + }) + } + + def keyOutput(s: State, option: Mode, sk: Def.ScopedKey[_]): String = { val extracted = Project.extract(s) import extracted._ option match { diff --git a/notes/1.2.0/inspect-alias.md b/notes/1.2.0/inspect-alias.md new file mode 100644 index 000000000..1840791f1 --- /dev/null +++ b/notes/1.2.0/inspect-alias.md @@ -0,0 +1,7 @@ +[@ruippeixotog]: https://github.com/gpoirier + +[#2881]: https://github.com/sbt/sbt/issues/2881 + +### Improvements + +- Adding minimal support for commands in inspect. There's also a special case for aliases. diff --git a/sbt/src/sbt-test/project/unified/build.sbt b/sbt/src/sbt-test/project/unified/build.sbt index 7f456b66c..966beab44 100644 --- a/sbt/src/sbt-test/project/unified/build.sbt +++ b/sbt/src/sbt-test/project/unified/build.sbt @@ -38,8 +38,8 @@ lazy val root = (project in file(".")) testFrameworks += new TestFramework("utest.runner.Framework"), commands += Command("inspectCheck", inspectBrief, inspectDetailed)(Inspect.parser) { - case (s, (option, sk)) => - val actual = Inspect.output(s, option, sk) + case (s, f) => + val actual = f() val expected = s"""Task: Unit Description: \tExecutes all tests.