From 4b88378c6192cc1ca3263e75f5cfd1c1b395f6cb Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 10 Feb 2017 13:58:05 +0000 Subject: [PATCH 01/11] Delete stale contraband classes This is due to sbt/contraband#75. --- .../codec/ConsolePromptEventFormats.scala | 27 ----------------- .../protocol/codec/ExectionEventFormats.scala | 29 ------------------- .../protocol/codec/StatusEventFormats.scala | 29 ------------------- 3 files changed, 85 deletions(-) delete mode 100644 protocol/src/main/contraband-scala/sbt/protocol/codec/ConsolePromptEventFormats.scala delete mode 100644 protocol/src/main/contraband-scala/sbt/protocol/codec/ExectionEventFormats.scala delete mode 100644 protocol/src/main/contraband-scala/sbt/protocol/codec/StatusEventFormats.scala diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/ConsolePromptEventFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/ConsolePromptEventFormats.scala deleted file mode 100644 index ed5f52417..000000000 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/ConsolePromptEventFormats.scala +++ /dev/null @@ -1,27 +0,0 @@ -/** - * This code is generated using sbt-datatype. - */ - -// DO NOT EDIT MANUALLY -package sbt.protocol.codec -import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait ConsolePromptEventFormats { self: sjsonnew.BasicJsonProtocol => -implicit lazy val ConsolePromptEventFormat: JsonFormat[sbt.protocol.ConsolePromptEvent] = new JsonFormat[sbt.protocol.ConsolePromptEvent] { - override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ConsolePromptEvent = { - jsOpt match { - case Some(js) => - unbuilder.beginObject(js) - - unbuilder.endObject() - sbt.protocol.ConsolePromptEvent() - case None => - deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.protocol.ConsolePromptEvent, builder: Builder[J]): Unit = { - builder.beginObject() - - builder.endObject() - } -} -} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/ExectionEventFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/ExectionEventFormats.scala deleted file mode 100644 index 5ba94d94e..000000000 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/ExectionEventFormats.scala +++ /dev/null @@ -1,29 +0,0 @@ -/** - * This code is generated using sbt-datatype. - */ - -// DO NOT EDIT MANUALLY -package sbt.protocol.codec -import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait ExectionEventFormats { self: sjsonnew.BasicJsonProtocol => -implicit lazy val ExectionEventFormat: JsonFormat[sbt.protocol.ExectionEvent] = new JsonFormat[sbt.protocol.ExectionEvent] { - override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ExectionEvent = { - jsOpt match { - case Some(js) => - unbuilder.beginObject(js) - val success = unbuilder.readField[String]("success") - val commandLine = unbuilder.readField[String]("commandLine") - unbuilder.endObject() - sbt.protocol.ExectionEvent(success, commandLine) - case None => - deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.protocol.ExectionEvent, builder: Builder[J]): Unit = { - builder.beginObject() - builder.addField("success", obj.success) - builder.addField("commandLine", obj.commandLine) - builder.endObject() - } -} -} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/StatusEventFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/StatusEventFormats.scala deleted file mode 100644 index 0819d1a22..000000000 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/StatusEventFormats.scala +++ /dev/null @@ -1,29 +0,0 @@ -/** - * This code is generated using sbt-datatype. - */ - -// DO NOT EDIT MANUALLY -package sbt.protocol.codec -import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait StatusEventFormats { self: sjsonnew.BasicJsonProtocol => -implicit lazy val StatusEventFormat: JsonFormat[sbt.protocol.StatusEvent] = new JsonFormat[sbt.protocol.StatusEvent] { - override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.StatusEvent = { - jsOpt match { - case Some(js) => - unbuilder.beginObject(js) - val status = unbuilder.readField[String]("status") - val commandQueue = unbuilder.readField[Vector[String]]("commandQueue") - unbuilder.endObject() - sbt.protocol.StatusEvent(status, commandQueue) - case None => - deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.protocol.StatusEvent, builder: Builder[J]): Unit = { - builder.beginObject() - builder.addField("status", obj.status) - builder.addField("commandQueue", obj.commandQueue) - builder.endObject() - } -} -} From 1ef59759cce5d8677bff2469f55121b69c531763 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 20 Jan 2017 16:34:38 +0000 Subject: [PATCH 02/11] Extract onExecCommand --- .../scala/sbt/internal/server/NetworkChannel.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 9ed98a6e3..7339b9985 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -70,10 +70,12 @@ final class NetworkChannel(val name: String, connection: Socket) extends Command out.flush() } - def onCommand(command: CommandMessage): Unit = - command match { - case x: ExecCommand => append(Exec(x.commandLine, x.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))) - } + def onCommand(command: CommandMessage): Unit = command match { + case x: ExecCommand => onExecCommand(x) + } + + private def onExecCommand(cmd: ExecCommand) = + append(Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))) def shutdown(): Unit = { println("Shutting down client connection") From 164b0fe83006b75fa83756b96d567b90b47fbb9a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 20 Jan 2017 16:35:00 +0000 Subject: [PATCH 03/11] Pass State to NetworkChannel --- main/src/main/scala/sbt/internal/CommandExchange.scala | 2 +- main/src/main/scala/sbt/internal/server/NetworkChannel.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 195384666..1d2ac3e7c 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -74,7 +74,7 @@ private[sbt] final class CommandExchange { def onIncomingSocket(socket: Socket): Unit = { s.log.info(s"new client connected from: ${socket.getPort}") - val channel = new NetworkChannel(newChannelName, socket) + val channel = new NetworkChannel(newChannelName, socket, s) subscribe(channel) channel.publishEventMessage(ChannelAcceptedEvent(channel.name)) } diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 7339b9985..20047277e 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -10,7 +10,7 @@ import java.util.concurrent.atomic.AtomicBoolean import sbt.protocol.{ Serialization, CommandMessage, ExecCommand, EventMessage } import sjsonnew.JsonFormat -final class NetworkChannel(val name: String, connection: Socket) extends CommandChannel { +final class NetworkChannel(val name: String, connection: Socket, state: State) extends CommandChannel { private val running = new AtomicBoolean(true) private val delimiter: Byte = '\n'.toByte private val out = connection.getOutputStream From d9d741851a113f9f5a44a6979d5f6e1ea74eb728 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 20 Jan 2017 16:35:06 +0000 Subject: [PATCH 04/11] Add and handle GetSetting --- .../sbt/internal/server/NetworkChannel.scala | 128 +++++++++++++++++- .../sbt/protocol/SettingQuery.scala | 32 +++++ .../sbt/protocol/SettingQueryResponse.scala | 32 +++++ .../codec/CommandMessageFormats.scala | 4 +- .../protocol/codec/EventMessageFormats.scala | 4 +- .../sbt/protocol/codec/JsonProtocol.scala | 2 + .../protocol/codec/SettingQueryFormats.scala | 27 ++++ .../codec/SettingQueryResponseFormats.scala | 27 ++++ protocol/src/main/contraband/server.contra | 9 ++ 9 files changed, 258 insertions(+), 7 deletions(-) create mode 100644 protocol/src/main/contraband-scala/sbt/protocol/SettingQuery.scala create mode 100644 protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala create mode 100644 protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 20047277e..9cf2698ed 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -7,8 +7,9 @@ package server import java.net.{ Socket, SocketTimeoutException } import java.util.concurrent.atomic.AtomicBoolean -import sbt.protocol.{ Serialization, CommandMessage, ExecCommand, EventMessage } -import sjsonnew.JsonFormat +import scala.util.{ Left, Right } +import sbt.protocol._ +import sjsonnew._, LList.:*: final class NetworkChannel(val name: String, connection: Socket, state: State) extends CommandChannel { private val running = new AtomicBoolean(true) @@ -71,15 +72,136 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e } def onCommand(command: CommandMessage): Unit = command match { - case x: ExecCommand => onExecCommand(x) + case x: ExecCommand => onExecCommand(x) + case x: SettingQuery => onSettingQuery(x) } private def onExecCommand(cmd: ExecCommand) = append(Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))) + private def onSettingQuery(req: SettingQuery) = { + import sbt.internal.util.complete.Parser + + val extracted = Project extract state + val keys = Parser.parse(req.setting, Act aggregatedKeyParser extracted) + + def getSettingValue[A](key: Def.ScopedKey[A]) = + extracted.structure.data.get(key.scope, key.key) + .toRight(s"Key ${Def displayFull key} not found") + .flatMap { + case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings") + case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings") + case x => Right(x) + } + + def zeroValues: Either[Vector[String], Vector[Any]] = Right(Vector.empty) + def anyLeftsOrAllRights[A, B](acc: Either[Vector[A], Vector[B]], elem: Either[A, B]): Either[Vector[A], Vector[B]] = + (acc, elem) match { + case (Right(a), Right(x)) => Right(a :+ x) + case (Right(_), Left(x)) => Left(Vector(x)) + case (Left(a), Right(_)) => Left(a) + case (Left(a), Left(x)) => Left(a :+ x) + } + + val values = keys match { + case Left(msg) => Left(s"Invalid programmatic input:" +: (msg.lines.toVector map (" " + _))) + case Right(keys) => keys.map(getSettingValue(_)).foldLeft(zeroValues)(anyLeftsOrAllRights) + } + + val jsonValues = values match { + case Left(errors) => errors + case Right(values) => values map (_.toString) + } + + StandardMain.exchange publishEventMessage SettingQueryResponse(jsonValues) + } + def shutdown(): Unit = { println("Shutting down client connection") running.set(false) out.close() } } + +trait SettingQueryInstances { + import BasicJsonProtocol._ + + type SettingQueryRepr = String :*: LNil + implicit def settingQueryIso: IsoLList.Aux[SettingQuery, SettingQueryRepr] = LList.iso( + (x => "settingKey" -> x.setting :*: LNil), + (x => SettingQuery(x.head)) + ) + + import sbt.internal.util._ + + type AttrKeyRepr[A] = String :*: Manifest[A] :*: Option[String] :*: Vector[AttributeKey[_]] :*: Boolean :*: Int :*: LNil + + // FIXME: Can't go this IsoLList way because AttributeKey depends on AttributeKey (extend) + implicit def attrKeyIso[A]: IsoLList.Aux[AttributeKey[A], AttrKeyRepr[A]] = ??? + // LList.iso[AttributeKey[A], AttrKeyRepr[A]](attrKeyToRepr, attrKeyFromRepr) + + // def attrKeyToRepr[A](x: AttributeKey[A]): AttrKeyRepr[A] = ( + // /* */ "label" -> x.label /* */ :*: + // /**/ "manifest" -> x.manifest /* */ :*: + // /* */ "desc" -> x.description /* */ :*: + // /* */ "extend" -> x.extend.toVector /**/ :*: + // /* */ "isLocal" -> x.isLocal /* */ :*: + // /* */ "rank" -> x.rank /* */ :*: + // LNil + // ) + + def attrKeyFromRepr[A](x: AttrKeyRepr[A]): AttributeKey[A] = { + val LCons("label", label, + LCons("manifest", manifest, + LCons("desc", desc, + LCons("extend", extend, + LCons("isLabel", isLocal, + LCons("rank", rank, LNil) + ))))) = x + if (isLocal) AttributeKey.local[A](manifest) + else desc match { + case Some(desc) => AttributeKey(label, desc, extend, rank)(manifest) + case None => extend match { + case Seq() => AttributeKey(label, rank)(manifest) + case _ => + // With the given API it's not possible to create an AttributeKey + // which extends other attribute keys without having a description + // But that's not enforced in the data types. So default description to "" + AttributeKey(label, "", extend, rank)(manifest) + } + } + } + + // TODO: or use AttributeKey label? (String) + // implicit def attrMapFormat: JsonFormat[AttributeMap] = project[AttributeMap, Map[AttributeKey[_], Any]]( + // attrMap => attrMap.entries.iterator.map(x => x.key -> x.value).toMap, + // map => AttributeMap(map.iterator.map { case (k: AttributeKey[kt], v) => AttributeEntry(k, v.asInstanceOf[kt]) }.toSeq) + // ) + + implicit def scopeAxisIso[A](implicit z: JsonFormat[A]): JsonFormat[ScopeAxis[A]] = + new JsonFormat[ScopeAxis[A]] { + def write[J](obj: ScopeAxis[A], builder: Builder[J]): Unit = obj match { + case This => builder writeString "This" + case Global => builder writeString "Global" + case Select(s) => z.write(s, builder) + } + def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): ScopeAxis[A] = jsOpt match { + case None => deserializationError("Expected some JSON but found None") + case Some("This") => This + case Some("Global") => Global + case Some(_) => Select(z.read(jsOpt, unbuilder)) + } + } + + type ScopeRepr = ScopeAxis[Reference] :*: ScopeAxis[ConfigKey] :*: ScopeAxis[AttributeKey[_]] :*: ScopeAxis[AttributeMap] :*: LNil + // implicit def scopeIso: IsoLList.Aux[Scope, ScopeRepr] = LList.iso[Scope, ScopeRepr]( + // { x: Scope => "project" -> x.project :*: "config" -> x.config :*: "task" -> x.task :*: "extra" -> x.extra :*: LNil }, + // { x: ScopeRepr => Scope(x.head, x.tail.head, x.tail.tail.head, x.tail.tail.tail.head) } + // ) + + type SettingKeyRepr[A] = Scope :*: AttributeKey[A] :*: LNil + // implicit def settingKeyIso[A]: IsoLList.Aux[SettingKey[A], SettingKeyRepr[A]] = LList.iso( + // { x: SettingKey[A] => "scope" -> x.scope :*: "attrKey" -> x.key :*: LNil }, + // { x: SettingKeyRepr[A] => Scoped.scopedSetting(x.head, x.tail.head) } + // ) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/SettingQuery.scala b/protocol/src/main/contraband-scala/sbt/protocol/SettingQuery.scala new file mode 100644 index 000000000..a0408cffb --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/SettingQuery.scala @@ -0,0 +1,32 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +final class SettingQuery private ( + val setting: String) extends sbt.protocol.CommandMessage() with Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: SettingQuery => (this.setting == x.setting) + case _ => false + } + override def hashCode: Int = { + 37 * (17 + setting.##) + } + override def toString: String = { + "SettingQuery(" + setting + ")" + } + protected[this] def copy(setting: String = setting): SettingQuery = { + new SettingQuery(setting) + } + def withSetting(setting: String): SettingQuery = { + copy(setting = setting) + } +} +object SettingQuery { + + def apply(setting: String): SettingQuery = new SettingQuery(setting) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala new file mode 100644 index 000000000..f89b2b6a3 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala @@ -0,0 +1,32 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +final class SettingQueryResponse private ( + val values: Vector[String]) extends sbt.protocol.EventMessage() with Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: SettingQueryResponse => (this.values == x.values) + case _ => false + } + override def hashCode: Int = { + 37 * (17 + values.##) + } + override def toString: String = { + "SettingQueryResponse(" + values + ")" + } + protected[this] def copy(values: Vector[String] = values): SettingQueryResponse = { + new SettingQueryResponse(values) + } + def withValues(values: Vector[String]): SettingQueryResponse = { + copy(values = values) + } +} +object SettingQueryResponse { + + def apply(values: Vector[String]): SettingQueryResponse = new SettingQueryResponse(values) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala index 3a747c4e3..5dc704932 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala @@ -5,6 +5,6 @@ // DO NOT EDIT MANUALLY package sbt.protocol.codec import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats => -implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat1[sbt.protocol.CommandMessage, sbt.protocol.ExecCommand]("type") +trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats => +implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat2[sbt.protocol.CommandMessage, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery]("type") } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala index 547cd7031..345b60771 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala @@ -5,6 +5,6 @@ // DO NOT EDIT MANUALLY package sbt.protocol.codec import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats => -implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat3[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent]("type") +trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.protocol.codec.SettingQueryResponseFormats => +implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat4[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQueryResponse]("type") } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala index fa9fb28ac..7741b72e7 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala @@ -6,10 +6,12 @@ package sbt.protocol.codec trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats + with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.CommandMessageFormats with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats + with sbt.protocol.codec.SettingQueryResponseFormats with sbt.protocol.codec.EventMessageFormats with sbt.protocol.codec.ExecutionEventFormats object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryFormats.scala new file mode 100644 index 000000000..774db9104 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryFormats.scala @@ -0,0 +1,27 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait SettingQueryFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val SettingQueryFormat: JsonFormat[sbt.protocol.SettingQuery] = new JsonFormat[sbt.protocol.SettingQuery] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQuery = { + jsOpt match { + case Some(js) => + unbuilder.beginObject(js) + val setting = unbuilder.readField[String]("setting") + unbuilder.endObject() + sbt.protocol.SettingQuery(setting) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.protocol.SettingQuery, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("setting", obj.setting) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala new file mode 100644 index 000000000..dfe1477fa --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala @@ -0,0 +1,27 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait SettingQueryResponseFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQueryResponse] = new JsonFormat[sbt.protocol.SettingQueryResponse] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQueryResponse = { + jsOpt match { + case Some(js) => + unbuilder.beginObject(js) + val values = unbuilder.readField[Vector[String]]("values") + unbuilder.endObject() + sbt.protocol.SettingQueryResponse(values) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.protocol.SettingQueryResponse, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("values", obj.values) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband/server.contra b/protocol/src/main/contraband/server.contra index 717b74d61..129f1bb91 100644 --- a/protocol/src/main/contraband/server.contra +++ b/protocol/src/main/contraband/server.contra @@ -13,6 +13,11 @@ type ExecCommand implements CommandMessage { execId: String @since("0.0.1") } +type SettingQuery implements CommandMessage { + setting: String! +} + + ## Message for events. interface EventMessage { } @@ -35,6 +40,10 @@ type ExecStatusEvent implements EventMessage { commandQueue: [String] } +type SettingQueryResponse implements EventMessage { + values: [String] +} + # enum Status { # Ready # Processing From 2efacb8c46657e2bb6f3707808be55522ce34146 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 10 Feb 2017 18:10:49 +0000 Subject: [PATCH 05/11] Delete SettingQuery instances we don't need .. now that there's a copy in the git history --- .../sbt/internal/server/NetworkChannel.scala | 85 +------------------ 1 file changed, 1 insertion(+), 84 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 9cf2698ed..a564e5044 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -9,7 +9,7 @@ import java.net.{ Socket, SocketTimeoutException } import java.util.concurrent.atomic.AtomicBoolean import scala.util.{ Left, Right } import sbt.protocol._ -import sjsonnew._, LList.:*: +import sjsonnew._ final class NetworkChannel(val name: String, connection: Socket, state: State) extends CommandChannel { private val running = new AtomicBoolean(true) @@ -122,86 +122,3 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e out.close() } } - -trait SettingQueryInstances { - import BasicJsonProtocol._ - - type SettingQueryRepr = String :*: LNil - implicit def settingQueryIso: IsoLList.Aux[SettingQuery, SettingQueryRepr] = LList.iso( - (x => "settingKey" -> x.setting :*: LNil), - (x => SettingQuery(x.head)) - ) - - import sbt.internal.util._ - - type AttrKeyRepr[A] = String :*: Manifest[A] :*: Option[String] :*: Vector[AttributeKey[_]] :*: Boolean :*: Int :*: LNil - - // FIXME: Can't go this IsoLList way because AttributeKey depends on AttributeKey (extend) - implicit def attrKeyIso[A]: IsoLList.Aux[AttributeKey[A], AttrKeyRepr[A]] = ??? - // LList.iso[AttributeKey[A], AttrKeyRepr[A]](attrKeyToRepr, attrKeyFromRepr) - - // def attrKeyToRepr[A](x: AttributeKey[A]): AttrKeyRepr[A] = ( - // /* */ "label" -> x.label /* */ :*: - // /**/ "manifest" -> x.manifest /* */ :*: - // /* */ "desc" -> x.description /* */ :*: - // /* */ "extend" -> x.extend.toVector /**/ :*: - // /* */ "isLocal" -> x.isLocal /* */ :*: - // /* */ "rank" -> x.rank /* */ :*: - // LNil - // ) - - def attrKeyFromRepr[A](x: AttrKeyRepr[A]): AttributeKey[A] = { - val LCons("label", label, - LCons("manifest", manifest, - LCons("desc", desc, - LCons("extend", extend, - LCons("isLabel", isLocal, - LCons("rank", rank, LNil) - ))))) = x - if (isLocal) AttributeKey.local[A](manifest) - else desc match { - case Some(desc) => AttributeKey(label, desc, extend, rank)(manifest) - case None => extend match { - case Seq() => AttributeKey(label, rank)(manifest) - case _ => - // With the given API it's not possible to create an AttributeKey - // which extends other attribute keys without having a description - // But that's not enforced in the data types. So default description to "" - AttributeKey(label, "", extend, rank)(manifest) - } - } - } - - // TODO: or use AttributeKey label? (String) - // implicit def attrMapFormat: JsonFormat[AttributeMap] = project[AttributeMap, Map[AttributeKey[_], Any]]( - // attrMap => attrMap.entries.iterator.map(x => x.key -> x.value).toMap, - // map => AttributeMap(map.iterator.map { case (k: AttributeKey[kt], v) => AttributeEntry(k, v.asInstanceOf[kt]) }.toSeq) - // ) - - implicit def scopeAxisIso[A](implicit z: JsonFormat[A]): JsonFormat[ScopeAxis[A]] = - new JsonFormat[ScopeAxis[A]] { - def write[J](obj: ScopeAxis[A], builder: Builder[J]): Unit = obj match { - case This => builder writeString "This" - case Global => builder writeString "Global" - case Select(s) => z.write(s, builder) - } - def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): ScopeAxis[A] = jsOpt match { - case None => deserializationError("Expected some JSON but found None") - case Some("This") => This - case Some("Global") => Global - case Some(_) => Select(z.read(jsOpt, unbuilder)) - } - } - - type ScopeRepr = ScopeAxis[Reference] :*: ScopeAxis[ConfigKey] :*: ScopeAxis[AttributeKey[_]] :*: ScopeAxis[AttributeMap] :*: LNil - // implicit def scopeIso: IsoLList.Aux[Scope, ScopeRepr] = LList.iso[Scope, ScopeRepr]( - // { x: Scope => "project" -> x.project :*: "config" -> x.config :*: "task" -> x.task :*: "extra" -> x.extra :*: LNil }, - // { x: ScopeRepr => Scope(x.head, x.tail.head, x.tail.tail.head, x.tail.tail.tail.head) } - // ) - - type SettingKeyRepr[A] = Scope :*: AttributeKey[A] :*: LNil - // implicit def settingKeyIso[A]: IsoLList.Aux[SettingKey[A], SettingKeyRepr[A]] = LList.iso( - // { x: SettingKey[A] => "scope" -> x.scope :*: "attrKey" -> x.key :*: LNil }, - // { x: SettingKeyRepr[A] => Scoped.scopedSetting(x.head, x.tail.head) } - // ) -} From f09897ca29c541fcb6e76f2f6b0f9ce6c98e7cbf Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Feb 2017 13:40:59 +0000 Subject: [PATCH 06/11] Drop aggregation in querying settings --- .../sbt/internal/server/NetworkChannel.scala | 21 ++++++------------- .../sbt/protocol/SettingQueryResponse.scala | 18 ++++++++-------- .../codec/SettingQueryResponseFormats.scala | 6 +++--- protocol/src/main/contraband/server.contra | 2 +- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index a564e5044..790091aa6 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -83,7 +83,7 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e import sbt.internal.util.complete.Parser val extracted = Project extract state - val keys = Parser.parse(req.setting, Act aggregatedKeyParser extracted) + val key = Parser.parse(req.setting, Act scopedKeyParser extracted) def getSettingValue[A](key: Def.ScopedKey[A]) = extracted.structure.data.get(key.scope, key.key) @@ -94,23 +94,14 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e case x => Right(x) } - def zeroValues: Either[Vector[String], Vector[Any]] = Right(Vector.empty) - def anyLeftsOrAllRights[A, B](acc: Either[Vector[A], Vector[B]], elem: Either[A, B]): Either[Vector[A], Vector[B]] = - (acc, elem) match { - case (Right(a), Right(x)) => Right(a :+ x) - case (Right(_), Left(x)) => Left(Vector(x)) - case (Left(a), Right(_)) => Left(a) - case (Left(a), Left(x)) => Left(a :+ x) - } - - val values = keys match { - case Left(msg) => Left(s"Invalid programmatic input:" +: (msg.lines.toVector map (" " + _))) - case Right(keys) => keys.map(getSettingValue(_)).foldLeft(zeroValues)(anyLeftsOrAllRights) + val values = key match { + case Left(msg) => Left(s"Invalid programmatic input: $msg") + case Right(key) => Right(getSettingValue(key)) } val jsonValues = values match { - case Left(errors) => errors - case Right(values) => values map (_.toString) + case Left(errors) => errors + case Right(value) => value.toString } StandardMain.exchange publishEventMessage SettingQueryResponse(jsonValues) diff --git a/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala index f89b2b6a3..ce844ff43 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala @@ -5,28 +5,28 @@ // DO NOT EDIT MANUALLY package sbt.protocol final class SettingQueryResponse private ( - val values: Vector[String]) extends sbt.protocol.EventMessage() with Serializable { + val value: String) extends sbt.protocol.EventMessage() with Serializable { override def equals(o: Any): Boolean = o match { - case x: SettingQueryResponse => (this.values == x.values) + case x: SettingQueryResponse => (this.value == x.value) case _ => false } override def hashCode: Int = { - 37 * (17 + values.##) + 37 * (17 + value.##) } override def toString: String = { - "SettingQueryResponse(" + values + ")" + "SettingQueryResponse(" + value + ")" } - protected[this] def copy(values: Vector[String] = values): SettingQueryResponse = { - new SettingQueryResponse(values) + protected[this] def copy(value: String = value): SettingQueryResponse = { + new SettingQueryResponse(value) } - def withValues(values: Vector[String]): SettingQueryResponse = { - copy(values = values) + def withValue(value: String): SettingQueryResponse = { + copy(value = value) } } object SettingQueryResponse { - def apply(values: Vector[String]): SettingQueryResponse = new SettingQueryResponse(values) + def apply(value: String): SettingQueryResponse = new SettingQueryResponse(value) } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala index dfe1477fa..b6555f1c6 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala @@ -11,16 +11,16 @@ implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQue jsOpt match { case Some(js) => unbuilder.beginObject(js) - val values = unbuilder.readField[Vector[String]]("values") + val value = unbuilder.readField[String]("value") unbuilder.endObject() - sbt.protocol.SettingQueryResponse(values) + sbt.protocol.SettingQueryResponse(value) case None => deserializationError("Expected JsObject but found None") } } override def write[J](obj: sbt.protocol.SettingQueryResponse, builder: Builder[J]): Unit = { builder.beginObject() - builder.addField("values", obj.values) + builder.addField("value", obj.value) builder.endObject() } } diff --git a/protocol/src/main/contraband/server.contra b/protocol/src/main/contraband/server.contra index 129f1bb91..44103c0c1 100644 --- a/protocol/src/main/contraband/server.contra +++ b/protocol/src/main/contraband/server.contra @@ -41,7 +41,7 @@ type ExecStatusEvent implements EventMessage { } type SettingQueryResponse implements EventMessage { - values: [String] + value: String! } # enum Status { From 206c3e6d4d3b07824a8f4f8eada2eccc853c6fc6 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Feb 2017 14:31:15 +0000 Subject: [PATCH 07/11] Introduce Def displayBuildRelative/showBuildRelativeKey --- main-settings/src/main/scala/sbt/Def.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/main-settings/src/main/scala/sbt/Def.scala b/main-settings/src/main/scala/sbt/Def.scala index 7f08e50bb..18062bb71 100644 --- a/main-settings/src/main/scala/sbt/Def.scala +++ b/main-settings/src/main/scala/sbt/Def.scala @@ -4,6 +4,7 @@ import sbt.internal.util.Types.const import sbt.internal.util.{ Attributed, AttributeKey, Init, Show } import sbt.internal.util.complete.Parser import java.io.File +import java.net.URI import Scope.{ ThisScope, GlobalScope } import KeyRanks.{ DTask, Invisible } @@ -26,12 +27,21 @@ object Def extends Init[Scope] with TaskMacroExtra { def apply(key: ScopedKey[_]) = Scope.display(key.scope, colored(key.key.label, keyNameColor), ref => displayRelative(current, multi, ref)) } + def showBuildRelativeKey(currentBuild: URI, multi: Boolean, keyNameColor: Option[String] = None): Show[ScopedKey[_]] = new Show[ScopedKey[_]] { + def apply(key: ScopedKey[_]) = + Scope.display(key.scope, colored(key.key.label, keyNameColor), ref => displayBuildRelative(currentBuild, multi, ref)) + } def displayRelative(current: ProjectRef, multi: Boolean, project: Reference): String = project match { case BuildRef(current.build) => "{.}/" case `current` => if (multi) current.project + "/" else "" case ProjectRef(current.build, x) => x + "/" case _ => Reference.display(project) + "/" } + def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String = project match { + case BuildRef(`currentBuild`) => "{.}/" + case ProjectRef(`currentBuild`, x) => x + "/" + case _ => Reference.display(project) + "/" + } def displayFull(scoped: ScopedKey[_]): String = displayFull(scoped, None) def displayFull(scoped: ScopedKey[_], keyNameColor: Option[String]): String = Scope.display(scoped.scope, colored(scoped.key.label, keyNameColor)) def displayMasked(scoped: ScopedKey[_], mask: ScopeMask): String = Scope.displayMasked(scoped.scope, scoped.key.label, mask) From b282ea51d73a2bd3ec44f63330da41f429738880 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Feb 2017 14:31:28 +0000 Subject: [PATCH 08/11] Extract Act.taskKeyExtra --- main/src/main/scala/sbt/internal/Act.scala | 38 +++++++++++++--------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/main/src/main/scala/sbt/internal/Act.scala b/main/src/main/scala/sbt/internal/Act.scala index 6be991063..eb82de0b4 100644 --- a/main/src/main/scala/sbt/internal/Act.scala +++ b/main/src/main/scala/sbt/internal/Act.scala @@ -36,26 +36,34 @@ object Act { def scopedKeyFull(index: KeyIndex, current: ProjectRef, defaultConfigs: Option[ResolvedReference] => Seq[String], keyMap: Map[String, AttributeKey[_]]): Parser[Seq[Parser[ParsedKey]]] = { - def taskKeyExtra(proj: Option[ResolvedReference], confAmb: ParsedAxis[String], baseMask: ScopeMask): Seq[Parser[ParsedKey]] = - for { - conf <- configs(confAmb, defaultConfigs, proj, index) - } yield for { - taskAmb <- taskAxis(conf, index.tasks(proj, conf), keyMap) - task = resolveTask(taskAmb) - key <- key(index, proj, conf, task, keyMap) - extra <- extraAxis(keyMap, IMap.empty) - } yield { - val mask = baseMask.copy(task = taskAmb.isExplicit, extra = true) - new ParsedKey(makeScopedKey(proj, conf, task, extra, key), mask) - } - for { rawProject <- optProjectRef(index, current) proj = resolveProject(rawProject, current) confAmb <- config(index configs proj) partialMask = ScopeMask(rawProject.isExplicit, confAmb.isExplicit, false, false) - } yield taskKeyExtra(proj, confAmb, partialMask) + } yield taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask) } + + def taskKeyExtra( + index: KeyIndex, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]], + proj: Option[ResolvedReference], + confAmb: ParsedAxis[String], + baseMask: ScopeMask + ): Seq[Parser[ParsedKey]] = + for { + conf <- configs(confAmb, defaultConfigs, proj, index) + } yield for { + taskAmb <- taskAxis(conf, index.tasks(proj, conf), keyMap) + task = resolveTask(taskAmb) + key <- key(index, proj, conf, task, keyMap) + extra <- extraAxis(keyMap, IMap.empty) + } yield { + val mask = baseMask.copy(task = taskAmb.isExplicit, extra = true) + new ParsedKey(makeScopedKey(proj, conf, task, extra, key), mask) + } + def makeScopedKey(proj: Option[ResolvedReference], conf: Option[String], task: Option[AttributeKey[_]], extra: ScopeAxis[AttributeMap], key: AttributeKey[_]): ScopedKey[_] = ScopedKey(Scope(toAxis(proj, Global), toAxis(conf map ConfigKey.apply, Global), toAxis(task, Global), extra), key) @@ -309,4 +317,4 @@ object Act { final object Omitted extends ParsedAxis[Nothing] final class ParsedValue[T](val value: T) extends ParsedAxis[T] def value[T](t: Parser[T]): Parser[ParsedAxis[T]] = t map { v => new ParsedValue(v) } -} \ No newline at end of file +} From bf5bc46d3c3e5c7104fb20eeadfba65d0c671bb2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Feb 2017 14:11:20 +0000 Subject: [PATCH 09/11] Disallow SettingQuery relying on currentProject Introduce a specialised scopedKeyParser on SettingQuery to redefine the "projectRef" parser to never match "*" or omitted project refereneces. --- .../sbt/internal/server/NetworkChannel.scala | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 790091aa6..33a75f6f3 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -83,7 +83,7 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e import sbt.internal.util.complete.Parser val extracted = Project extract state - val key = Parser.parse(req.setting, Act scopedKeyParser extracted) + val key = Parser.parse(req.setting, SettingQuery scopedKeyParser extracted) def getSettingValue[A](key: Def.ScopedKey[A]) = extracted.structure.data.get(key.scope, key.key) @@ -113,3 +113,61 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e out.close() } } + +object SettingQuery { + import java.net.URI + import sbt.internal.util.{ AttributeKey, Settings } + import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._ + import sbt.Def.{ showBuildRelativeKey, ScopedKey } + + // Similar to Act.projectRef, except doesn't match "*" or omitted project references + def projectRef(index: KeyIndex, currentBuild: URI): Parser[ResolvedReference] = { + val trailing = '/' !!! "Expected '/' (if selecting a project)" + Act.resolvedReference(index, currentBuild, trailing) + } + + def scopedKeyFull( + index: KeyIndex, + currentBuild: URI, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]] + ): Parser[Seq[Parser[ParsedKey]]] = { + for { + proj <- projectRef(index, currentBuild) + confAmb <- Act.config(index configs Some(proj)) + partialMask = ScopeMask(true, confAmb.isExplicit, false, false) + } yield Act.taskKeyExtra(index, defaultConfigs, keyMap, Some(proj), confAmb, partialMask) + } + + def scopedKeyParser(structure: BuildStructure, currentBuild: URI): Parser[ScopedKey[_]] = + scopedKey( + structure.index.keyIndex, + currentBuild, + structure.extra.configurationsForAxis, + structure.index.keyMap, + structure.data + ) + + def scopedKeySelected( + index: KeyIndex, + currentBuild: URI, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]], + data: Settings[Scope] + ): Parser[ParsedKey] = + scopedKeyFull(index, currentBuild, defaultConfigs, keyMap) flatMap { choices => + Act.select(choices, data)(showBuildRelativeKey(currentBuild, index.buildURIs.size > 1)) + } + + def scopedKey( + index: KeyIndex, + currentBuild: URI, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]], + data: Settings[Scope] + ): Parser[ScopedKey[_]] = + scopedKeySelected(index, currentBuild, defaultConfigs, keyMap, data).map(_.key) + + def scopedKeyParser(extracted: Extracted): Parser[ScopedKey[_]] = + scopedKeyParser(extracted.structure, extracted.currentRef.build) +} From 07e2da9d85041a371343f719e76aee3a7e6104f2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Feb 2017 15:01:44 +0000 Subject: [PATCH 10/11] Don't breach responsibility, NetworkChannel Only depend on BuildStructure and currentBuild, not the whole of State! --- .../main/scala/sbt/internal/CommandExchange.scala | 2 +- .../scala/sbt/internal/server/NetworkChannel.scala | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 1d2ac3e7c..e419f5213 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -74,7 +74,7 @@ private[sbt] final class CommandExchange { def onIncomingSocket(socket: Socket): Unit = { s.log.info(s"new client connected from: ${socket.getPort}") - val channel = new NetworkChannel(newChannelName, socket, s) + val channel = new NetworkChannel(newChannelName, socket, Project structure s, Project.session(s).currentBuild) subscribe(channel) channel.publishEventMessage(ChannelAcceptedEvent(channel.name)) } diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 33a75f6f3..f178ebe3c 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -5,13 +5,13 @@ package sbt package internal package server -import java.net.{ Socket, SocketTimeoutException } +import java.net.{ Socket, SocketTimeoutException, URI } import java.util.concurrent.atomic.AtomicBoolean import scala.util.{ Left, Right } import sbt.protocol._ import sjsonnew._ -final class NetworkChannel(val name: String, connection: Socket, state: State) extends CommandChannel { +final class NetworkChannel(val name: String, connection: Socket, structure: BuildStructure, currentBuild: URI) extends CommandChannel { private val running = new AtomicBoolean(true) private val delimiter: Byte = '\n'.toByte private val out = connection.getOutputStream @@ -82,11 +82,10 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e private def onSettingQuery(req: SettingQuery) = { import sbt.internal.util.complete.Parser - val extracted = Project extract state - val key = Parser.parse(req.setting, SettingQuery scopedKeyParser extracted) + val key = Parser.parse(req.setting, SettingQuery.scopedKeyParser(structure, currentBuild)) def getSettingValue[A](key: Def.ScopedKey[A]) = - extracted.structure.data.get(key.scope, key.key) + structure.data.get(key.scope, key.key) .toRight(s"Key ${Def displayFull key} not found") .flatMap { case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings") @@ -115,7 +114,6 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e } object SettingQuery { - import java.net.URI import sbt.internal.util.{ AttributeKey, Settings } import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._ import sbt.Def.{ showBuildRelativeKey, ScopedKey } @@ -167,7 +165,4 @@ object SettingQuery { data: Settings[Scope] ): Parser[ScopedKey[_]] = scopedKeySelected(index, currentBuild, defaultConfigs, keyMap, data).map(_.key) - - def scopedKeyParser(extracted: Extracted): Parser[ScopedKey[_]] = - scopedKeyParser(extracted.structure, extracted.currentRef.build) } From 5cedfab81e7e943c2d16c0e65143f23b40e348ac Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Feb 2017 15:15:35 +0000 Subject: [PATCH 11/11] Bring back global project-axis reference --- .../sbt/internal/server/NetworkChannel.scala | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index f178ebe3c..953a2d57a 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -118,10 +118,22 @@ object SettingQuery { import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._ import sbt.Def.{ showBuildRelativeKey, ScopedKey } - // Similar to Act.projectRef, except doesn't match "*" or omitted project references - def projectRef(index: KeyIndex, currentBuild: URI): Parser[ResolvedReference] = { + // Similar to Act.ParsedAxis / Act.projectRef / Act.resolveProject except you can't omit the project reference + + sealed trait ParsedExplicitAxis[+T] + final object ParsedExplicitGlobal extends ParsedExplicitAxis[Nothing] + final class ParsedExplicitValue[T](val value: T) extends ParsedExplicitAxis[T] + def explicitValue[T](t: Parser[T]): Parser[ParsedExplicitAxis[T]] = t map { v => new ParsedExplicitValue(v) } + + def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedExplicitAxis[ResolvedReference]] = { + val global = token(Act.GlobalString ~ '/') ^^^ ParsedExplicitGlobal val trailing = '/' !!! "Expected '/' (if selecting a project)" - Act.resolvedReference(index, currentBuild, trailing) + global | explicitValue(Act.resolvedReference(index, currentBuild, trailing)) + } + + def resolveProject(parsed: ParsedExplicitAxis[ResolvedReference]): Option[ResolvedReference] = parsed match { + case ParsedExplicitGlobal => None + case pv: ParsedExplicitValue[_] => Some(pv.value) } def scopedKeyFull( @@ -131,10 +143,11 @@ object SettingQuery { keyMap: Map[String, AttributeKey[_]] ): Parser[Seq[Parser[ParsedKey]]] = { for { - proj <- projectRef(index, currentBuild) - confAmb <- Act.config(index configs Some(proj)) + rawProject <- projectRef(index, currentBuild) + proj = resolveProject(rawProject) + confAmb <- Act.config(index configs proj) partialMask = ScopeMask(true, confAmb.isExplicit, false, false) - } yield Act.taskKeyExtra(index, defaultConfigs, keyMap, Some(proj), confAmb, partialMask) + } yield Act.taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask) } def scopedKeyParser(structure: BuildStructure, currentBuild: URI): Parser[ScopedKey[_]] =