From 871ccb0ab9bf26b1f96e6ded8dd81a399453fb92 Mon Sep 17 00:00:00 2001 From: Lukasz Piepiora Date: Wed, 25 Jun 2014 21:19:17 +0200 Subject: [PATCH] Fixes #1416: Plugin command doesn't work Fix `plugin` command not working with plugin names containing `.` --- main/src/main/scala/sbt/Act.scala | 5 + main/src/main/scala/sbt/Main.scala | 2 +- main/src/test/scala/PluginCommandTest.scala | 116 ++++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 main/src/test/scala/PluginCommandTest.scala diff --git a/main/src/main/scala/sbt/Act.scala b/main/src/main/scala/sbt/Act.scala index e65205754..0e5059105 100644 --- a/main/src/main/scala/sbt/Act.scala +++ b/main/src/main/scala/sbt/Act.scala @@ -191,6 +191,11 @@ object Act { def knownIDParser[T](knownKeys: Map[String, T], label: String): Parser[T] = token(examplesStrict(ID, knownKeys.keys.toSet, label)) map knownKeys + def knownPluginParser[T](knownPlugins: Map[String, T], label: String): Parser[T] = { + val pluginLabelParser = rep1sep(ID, '.').map(_.mkString(".")) + token(examplesStrict(pluginLabelParser, knownPlugins.keys.toSet, label)) map knownPlugins + } + def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedAxis[ResolvedReference]] = { val global = token(GlobalString ~ '/') ^^^ ParsedGlobal diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 6d437d4b3..cd21f80b2 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -378,7 +378,7 @@ object BuiltinCommands { } val pluginParser: State => Parser[AutoPlugin] = s => { val autoPlugins: Map[String, AutoPlugin] = PluginsDebug.autoPluginMap(s) - token(Space) ~> Act.knownIDParser(autoPlugins, "plugin") + token(Space) ~> Act.knownPluginParser(autoPlugins, "plugin") } def plugin = Command(PluginCommand)(pluginParser) { (s, plugin) => val helpString = PluginsDebug.help(plugin, s) diff --git a/main/src/test/scala/PluginCommandTest.scala b/main/src/test/scala/PluginCommandTest.scala new file mode 100644 index 000000000..c118a5232 --- /dev/null +++ b/main/src/test/scala/PluginCommandTest.scala @@ -0,0 +1,116 @@ +package sbt + +import java.io._ + +import org.specs2.mutable.Specification + +object PluginCommandTestPlugin0 extends AutoPlugin + +package subpackage { + +object PluginCommandTestPlugin1 extends AutoPlugin + +} + +object PluginCommandTest extends Specification { + sequential + + import subpackage._ + import FakeState._ + + "The `plugin` command" should { + + "should work for plugins within nested in one package" in { + val output = processCommand("plugin sbt.PluginCommandTestPlugin0", PluginCommandTestPlugin0, PluginCommandTestPlugin1) + output must contain("sbt.PluginCommandTestPlugin0 is activated.") + } + + "should work for plugins nested more than one package" in { + val output = processCommand("plugin sbt.subpackage.PluginCommandTestPlugin1", PluginCommandTestPlugin0, PluginCommandTestPlugin1) + output must contain("sbt.subpackage.PluginCommandTestPlugin1 is activated.") + } + + "suggest a plugin when given an incorrect plugin with a similar name" in { + val output = processCommand("plugin PluginCommandTestPlugin0", PluginCommandTestPlugin0, PluginCommandTestPlugin1) + output must contain( + "Not a valid plugin: PluginCommandTestPlugin0 (similar: sbt.PluginCommandTestPlugin0, sbt.subpackage.PluginCommandTestPlugin1)" + ) + } + + } + +} + +object FakeState { + + def processCommand(input: String, enabledPlugins: AutoPlugin*): String = { + val previousOut = System.out + val outBuffer = new ByteArrayOutputStream + try { + System.setOut(new PrintStream(outBuffer, true)) + val state = FakeState(enabledPlugins: _*) + Command.process(input, state) + new String(outBuffer.toByteArray) + } finally { + System.setOut(previousOut) + } + } + + def apply(plugins: AutoPlugin*) = { + + val base = new File("").getAbsoluteFile + val testProject = Project("test-project", base).setAutoPlugins(plugins) + + val settings: Seq[Def.Setting[_]] = Nil + + val currentProject = Map(testProject.base.toURI -> testProject.id) + val currentEval: () => sbt.compiler.Eval = () => Load.mkEval(Nil, base, Nil) + val sessionSettings = SessionSettings(base.toURI, currentProject, Nil, Map.empty, Nil, currentEval) + + val delegates: (Scope) => Seq[Scope] = _ => Nil + val scopeLocal: Def.ScopeLocal = _ => Nil + + val data: Settings[Scope] = Def.make(settings)(delegates, scopeLocal, Def.showFullKey) + val extra: KeyIndex => BuildUtil[_] = (keyIndex) => BuildUtil(base.toURI, Map.empty, keyIndex, data) + val structureIndex: StructureIndex = Load.structureIndex(data, settings, extra, Map.empty) + val streams: (State) => BuildStreams.Streams = null + + val loadedDefinitions: LoadedDefinitions = new LoadedDefinitions( + base, Nil, ClassLoader.getSystemClassLoader, Nil, Seq(testProject), Nil + ) + + val pluginData = PluginData(Nil, Nil, None, None, Nil) + val detectedModules: DetectedModules[Plugin] = new DetectedModules(Nil) + val builds: DetectedModules[Build] = new DetectedModules[Build](Nil) + + val detectedAutoPlugins: Seq[DetectedAutoPlugin] = plugins.map(p => DetectedAutoPlugin(p.label, p, hasAutoImport = false)) + val detectedPlugins = new DetectedPlugins(detectedModules, detectedAutoPlugins, builds) + val loadedPlugins = new LoadedPlugins(base, pluginData, ClassLoader.getSystemClassLoader, detectedPlugins) + val buildUnit = new BuildUnit(base.toURI, base, loadedDefinitions, loadedPlugins) + + val (partBuildUnit: PartBuildUnit, _) = Load.loaded(buildUnit) + val loadedBuildUnit = Load.resolveProjects(base.toURI, partBuildUnit, _ => testProject.id) + + val units = Map(base.toURI -> loadedBuildUnit) + val buildStructure = new BuildStructure(units, base.toURI, settings, data, structureIndex, streams, delegates, scopeLocal) + + val attributes = AttributeMap.empty ++ AttributeMap( + AttributeEntry(Keys.sessionSettings, sessionSettings), + AttributeEntry(Keys.stateBuildStructure, buildStructure) + ) + + State( + null, + Seq(BuiltinCommands.plugin), + Set.empty, + None, + Seq.empty, + State.newHistory, + attributes, + GlobalLogging.initial(MainLogging.globalDefault(ConsoleOut.systemOut), File.createTempFile("sbt", ".log"), ConsoleOut.systemOut), + State.Continue + ) + + } + +}