mirror of https://github.com/sbt/sbt.git
Improve watch messages
This commit reworks the watch start message so that instead of printing something like: [info] [watch] 1. Waiting for source changes... (press 'r' to re-run the command, 'x' to exit sbt or 'enter' to return to the shell) it instead prints something like: [info] 1. Monitoring source files for updates... [info] Project: filesJVM [info] Command: compile [info] Options: [info] <enter>: return to the shell [info] 'r': repeat the current command [info] 'x': exit sbt It will also print which path triggered the build.
This commit is contained in:
parent
c72005fd2b
commit
247d242008
|
|
@ -118,11 +118,12 @@ object Watched {
|
|||
// Deprecated apis below
|
||||
@deprecated("unused", "1.3.0")
|
||||
def projectWatchingMessage(projectId: String): WatchState => String =
|
||||
((ws: WatchState) => projectOnWatchMessage(projectId)(ws.count).get)
|
||||
((ws: WatchState) => projectOnWatchMessage(projectId)(ws.count, projectId, Nil).get)
|
||||
.label("Watched.projectWatchingMessage")
|
||||
@deprecated("unused", "1.3.0")
|
||||
def projectOnWatchMessage(project: String): Int => Option[String] = { (count: Int) =>
|
||||
Some(s"$count. ${waitMessage(s" in project $project")}")
|
||||
def projectOnWatchMessage(project: String): (Int, String, Seq[String]) => Option[String] = {
|
||||
(count: Int, _: String, _: Seq[String]) =>
|
||||
Some(s"$count. ${waitMessage(s" in project $project")}")
|
||||
}.label("Watched.projectOnWatchMessage")
|
||||
|
||||
@deprecated("This method is not used and may be removed in a future version of sbt", "1.3.0")
|
||||
|
|
|
|||
|
|
@ -625,7 +625,6 @@ object Defaults extends BuildCommon {
|
|||
clean := Clean.taskIn(ThisScope).value,
|
||||
consoleProject := consoleProjectTask.value,
|
||||
watchTransitiveSources := watchTransitiveSourcesTask.value,
|
||||
watchStartMessage := Watched.projectOnWatchMessage(thisProjectRef.value.project),
|
||||
watch := watchSetting.value,
|
||||
fileOutputs += target.value ** AllPassFilter,
|
||||
transitiveGlobs := InputGraph.task.value,
|
||||
|
|
|
|||
|
|
@ -114,11 +114,11 @@ object Keys {
|
|||
val watchOnIteration = settingKey[Int => Watch.Action]("Function that is invoked before waiting for file system events or user input events. This is only invoked if watchOnStart is not explicitly set.").withRank(DSetting)
|
||||
val watchOnStart = settingKey[Continuous.Arguments => () => Watch.Action]("Function is invoked before waiting for file system or input events. The returned Action is used to either trigger the build, terminate the watch or wait for events.").withRank(DSetting)
|
||||
val watchService = settingKey[() => WatchService]("Service to use to monitor file system changes.").withRank(BMinusSetting).withRank(DSetting)
|
||||
val watchStartMessage = settingKey[Int => Option[String]]("The message to show when triggered execution waits for sources to change. The parameter is the current watch iteration count.").withRank(DSetting)
|
||||
val watchStartMessage = settingKey[(Int, String, Seq[String]) => Option[String]]("The message to show when triggered execution waits for sources to change. The parameters are the current watch iteration count, the current project name and the tasks that are being run with each build.").withRank(DSetting)
|
||||
// The watchTasks key should really be named watch, but that is already taken by the deprecated watch key. I'd be surprised if there are any plugins that use it so I think we should consider breaking binary compatibility to rename this task.
|
||||
val watchTasks = InputKey[StateTransform]("watch", "Watch a task (or multiple tasks) and rebuild when its file inputs change or user input is received. The semantics are more or less the same as the `~` command except that it cannot transform the state on exit. This means that it cannot be used to reload the build.").withRank(DSetting)
|
||||
val watchTrackMetaBuild = settingKey[Boolean]("Toggles whether or not changing the build files (e.g. **/*.sbt, project/**/(*.scala | *.java)) should automatically trigger a project reload").withRank(DSetting)
|
||||
val watchTriggeredMessage = settingKey[(Int, Event[FileAttributes]) => Option[String]]("The message to show before triggered execution executes an action after sources change. The parameters are the path that triggered the build and the current watch iteration count.").withRank(DSetting)
|
||||
val watchTriggeredMessage = settingKey[(Int, Event[FileAttributes], Seq[String]) => Option[String]]("The message to show before triggered execution executes an action after sources change. The parameters are the path that triggered the build and the current watch iteration count.").withRank(DSetting)
|
||||
|
||||
// Deprecated watch apis
|
||||
@deprecated("This is no longer used for continuous execution", "1.3.0")
|
||||
|
|
|
|||
|
|
@ -145,9 +145,12 @@ object Watch {
|
|||
|
||||
/**
|
||||
* Action that indicates that we should exit and run the provided command.
|
||||
*
|
||||
* @param commands the commands to run after we exit the watch
|
||||
*/
|
||||
final class Run(val commands: String*) extends CancelWatch
|
||||
final class Run(val commands: String*) extends CancelWatch {
|
||||
override def toString: String = s"Run(${commands.mkString(", ")})"
|
||||
}
|
||||
// For now leave this private in case this isn't the best unapply type signature since it can't
|
||||
// be evolved in a binary compatible way.
|
||||
private object Run {
|
||||
|
|
@ -289,33 +292,42 @@ object Watch {
|
|||
|
||||
/**
|
||||
* Converts user input to an Action with the following rules:
|
||||
* 1) on all platforms, new lines exit the watch
|
||||
* 2) on posix platforms, 'r' or 'R' will trigger a build
|
||||
* 3) on posix platforms, 's' or 'S' will exit the watch and run the shell command. This is to
|
||||
* support the case where the user starts sbt in a continuous mode but wants to return to
|
||||
* the shell without having to restart sbt.
|
||||
* 1) 'x' or 'X' will exit sbt
|
||||
* 2) 'r' or 'R' will trigger a build
|
||||
* 3) new line characters cancel the watch and return to the shell
|
||||
*/
|
||||
final val defaultInputParser: Parser[Action] = {
|
||||
def posixOnly(legal: String, action: Action): Parser[Action] =
|
||||
if (!Util.isWindows) chars(legal) ^^^ action
|
||||
else Parser.invalid(Seq("Can't use jline for individual character entry on windows."))
|
||||
val rebuildParser: Parser[Action] = posixOnly(legal = "rR", Trigger)
|
||||
val shellParser: Parser[Action] = posixOnly(legal = "sS", new Run("shell"))
|
||||
val cancelParser: Parser[Action] = chars(legal = "\n\r") ^^^ CancelWatch
|
||||
shellParser | rebuildParser | cancelParser
|
||||
val exitParser: Parser[Action] = chars("xX") ^^^ new Run("exit")
|
||||
val rebuildParser: Parser[Action] = chars("rR") ^^^ Trigger
|
||||
val cancelParser: Parser[Action] = chars(legal = "\n\r") ^^^ new Run("iflast shell")
|
||||
exitParser | rebuildParser | cancelParser
|
||||
}
|
||||
|
||||
private[this] val reRun =
|
||||
if (Util.isWindows) "" else ", 'r' to re-run the command or 's' to return to the shell"
|
||||
private[sbt] def waitMessage(project: String): String =
|
||||
s"Waiting for source changes$project... (press enter to interrupt$reRun)"
|
||||
private[this] val options = {
|
||||
val enter = "<enter>"
|
||||
val newLine = if (Util.isWindows) enter else ""
|
||||
val opts = Seq(
|
||||
s"$enter: return to the shell",
|
||||
s"'r$newLine': repeat the current command",
|
||||
s"'x$newLine': exit sbt"
|
||||
)
|
||||
s"Options:\n${opts.mkString(" ", "\n ", "")}"
|
||||
}
|
||||
private def waitMessage(project: String, commands: Seq[String]): String = {
|
||||
val plural = if (commands.size > 1) "s" else ""
|
||||
val cmds = commands.mkString("; ")
|
||||
s"Monitoring source files for updates...\n" +
|
||||
s"Project: $project\nCommand$plural: $cmds\n$options"
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that prints out the current iteration count and gives instructions for exiting
|
||||
* or triggering the build.
|
||||
*/
|
||||
val defaultStartWatch: Int => Option[String] =
|
||||
((count: Int) => Some(s"$count. ${waitMessage("")}")).label("Watched.defaultStartWatch")
|
||||
val defaultStartWatch: (Int, String, Seq[String]) => Option[String] = {
|
||||
(count: Int, project: String, commands: Seq[String]) =>
|
||||
Some(s"$count. ${waitMessage(project, commands)}")
|
||||
}.label("Watched.defaultStartWatch")
|
||||
|
||||
/**
|
||||
* Default no-op callback.
|
||||
|
|
@ -325,7 +337,8 @@ object Watch {
|
|||
private[sbt] val defaultCommandOnTermination: (Action, String, Int, State) => State =
|
||||
onTerminationImpl(ContinuousExecutePrefix).label("Watched.defaultCommandOnTermination")
|
||||
private[sbt] val defaultTaskOnTermination: (Action, String, Int, State) => State =
|
||||
onTerminationImpl("watch", ContinuousExecutePrefix).label("Watched.defaultTaskOnTermination")
|
||||
onTerminationImpl("watch", ContinuousExecutePrefix)
|
||||
.label("Watched.defaultTaskOnTermination")
|
||||
|
||||
/**
|
||||
* Default handler to transform the state when the watch terminates. When the [[Watch.Action]]
|
||||
|
|
@ -356,8 +369,15 @@ object Watch {
|
|||
* `Keys.watchTriggeredMessage := Watched.defaultOnTriggerMessage`, then nothing is logged when
|
||||
* a build is triggered.
|
||||
*/
|
||||
final val defaultOnTriggerMessage: (Int, Event[FileAttributes]) => Option[String] =
|
||||
((_: Int, _: Event[FileAttributes]) => None).label("Watched.defaultOnTriggerMessage")
|
||||
final val defaultOnTriggerMessage: (Int, Event[FileAttributes], Seq[String]) => Option[String] =
|
||||
((_: Int, e: Event[FileAttributes], commands: Seq[String]) => {
|
||||
val msg = s"Build triggered by ${e.entry.typedPath.toPath}. " +
|
||||
s"Running ${commands.mkString("'", "; ", "'")}."
|
||||
Some(msg)
|
||||
}).label("Watched.defaultOnTriggerMessage")
|
||||
|
||||
final val noTriggerMessage: (Int, Event[FileAttributes], Seq[String]) => Option[String] =
|
||||
(_, _, _) => None
|
||||
|
||||
/**
|
||||
* The minimum delay between file system polling when a `PollingWatchService` is used.
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ object Continuous extends DeprecatedContinuous {
|
|||
}
|
||||
|
||||
private[sbt] def setup[R](state: State, command: String)(
|
||||
f: (State, Seq[(String, State, () => Boolean)], Seq[String]) => R
|
||||
f: (Seq[String], State, Seq[(String, State, () => Boolean)], Seq[String]) => R
|
||||
): R = {
|
||||
// First set up the state so that we can capture whether or not a task completed successfully
|
||||
// or if it threw an Exception (we lose the actual exception, but that should still be printed
|
||||
|
|
@ -273,7 +273,7 @@ object Continuous extends DeprecatedContinuous {
|
|||
case Left(c) => (i :+ c, v)
|
||||
}
|
||||
}
|
||||
f(s, valid, invalid)
|
||||
f(commands, s, valid, invalid)
|
||||
}
|
||||
|
||||
private[sbt] def runToTermination(
|
||||
|
|
@ -283,14 +283,14 @@ object Continuous extends DeprecatedContinuous {
|
|||
isCommand: Boolean
|
||||
): State = Watch.withCharBufferedStdIn { in =>
|
||||
val duped = new DupedInputStream(in)
|
||||
setup(state.put(DupedSystemIn, duped), command) { (s, valid, invalid) =>
|
||||
setup(state.put(DupedSystemIn, duped), command) { (commands, s, valid, invalid) =>
|
||||
implicit val extracted: Extracted = Project.extract(s)
|
||||
EvaluateTask.withStreams(extracted.structure, s)(_.use(Keys.streams in Global) { streams =>
|
||||
implicit val logger: Logger = streams.log
|
||||
if (invalid.isEmpty) {
|
||||
val currentCount = new AtomicInteger(count)
|
||||
val configs = getAllConfigs(valid.map(v => v._1 -> v._2))
|
||||
val callbacks = aggregate(configs, logger, in, s, currentCount, isCommand)
|
||||
val callbacks = aggregate(configs, logger, in, s, currentCount, isCommand, commands)
|
||||
val task = () => {
|
||||
currentCount.getAndIncrement()
|
||||
// abort as soon as one of the tasks fails
|
||||
|
|
@ -312,8 +312,8 @@ object Continuous extends DeprecatedContinuous {
|
|||
} else {
|
||||
// At least one of the commands in the multi command string could not be parsed, so we
|
||||
// log an error and exit.
|
||||
val commands = invalid.mkString("'", "', '", "'")
|
||||
logger.error(s"Terminating watch due to invalid command(s): $commands")
|
||||
val invalidCommands = invalid.mkString("'", "', '", "'")
|
||||
logger.error(s"Terminating watch due to invalid command(s): $invalidCommands")
|
||||
state.fail
|
||||
}
|
||||
})
|
||||
|
|
@ -378,16 +378,18 @@ object Continuous extends DeprecatedContinuous {
|
|||
inputStream: InputStream,
|
||||
state: State,
|
||||
count: AtomicInteger,
|
||||
isCommand: Boolean
|
||||
isCommand: Boolean,
|
||||
commands: Seq[String]
|
||||
)(
|
||||
implicit extracted: Extracted
|
||||
): Callbacks = {
|
||||
val project = extracted.currentRef.project
|
||||
val logger = setLevel(rawLogger, configs.map(_.watchSettings.logLevel).min, state)
|
||||
val onEnter = () => configs.foreach(_.watchSettings.onEnter())
|
||||
val onStart: () => Watch.Action = getOnStart(configs, logger, count)
|
||||
val onStart: () => Watch.Action = getOnStart(project, commands, configs, rawLogger, count)
|
||||
val nextInputEvent: () => Watch.Action = parseInputEvents(configs, state, inputStream, logger)
|
||||
val (nextFileEvent, cleanupFileMonitor): (() => Watch.Action, () => Unit) =
|
||||
getFileEvents(configs, logger, state, count)
|
||||
val (nextFileEvent, cleanupFileMonitor): (() => Option[(Event, Watch.Action)], () => Unit) =
|
||||
getFileEvents(configs, rawLogger, state, count, commands)
|
||||
val nextEvent: () => Watch.Action =
|
||||
combineInputAndFileEvents(nextInputEvent, nextFileEvent, logger)
|
||||
val onExit = () => {
|
||||
|
|
@ -415,6 +417,8 @@ object Continuous extends DeprecatedContinuous {
|
|||
}
|
||||
|
||||
private def getOnStart(
|
||||
project: String,
|
||||
commands: Seq[String],
|
||||
configs: Seq[Config],
|
||||
logger: Logger,
|
||||
count: AtomicInteger
|
||||
|
|
@ -426,8 +430,9 @@ object Continuous extends DeprecatedContinuous {
|
|||
if (configs.size == 1) { // Only allow custom start messages for single tasks
|
||||
ws.startMessage match {
|
||||
case Some(Left(sm)) => logger.info(sm(params.watchState(count.get())))
|
||||
case Some(Right(sm)) => sm(count.get()).foreach(logger.info(_))
|
||||
case None => Watch.defaultStartWatch(count.get()).foreach(logger.info(_))
|
||||
case Some(Right(sm)) => sm(count.get(), project, commands).foreach(logger.info(_))
|
||||
case None =>
|
||||
Watch.defaultStartWatch(count.get(), project, commands).foreach(logger.info(_))
|
||||
}
|
||||
}
|
||||
Watch.Ignore
|
||||
|
|
@ -438,7 +443,8 @@ object Continuous extends DeprecatedContinuous {
|
|||
{
|
||||
val res = f.view.map(_()).min
|
||||
// Print the default watch message if there are multiple tasks
|
||||
if (configs.size > 1) Watch.defaultStartWatch(count.get()).foreach(logger.info(_))
|
||||
if (configs.size > 1)
|
||||
Watch.defaultStartWatch(count.get(), project, commands).foreach(logger.info(_))
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
@ -447,40 +453,14 @@ object Continuous extends DeprecatedContinuous {
|
|||
logger: Logger,
|
||||
state: State,
|
||||
count: AtomicInteger,
|
||||
)(implicit extracted: Extracted): (() => Watch.Action, () => Unit) = {
|
||||
commands: Seq[String]
|
||||
)(implicit extracted: Extracted): (() => Option[(Event, Watch.Action)], () => Unit) = {
|
||||
val trackMetaBuild = configs.forall(_.watchSettings.trackMetaBuild)
|
||||
val buildGlobs =
|
||||
if (trackMetaBuild) extracted.getOpt(Keys.fileInputs in Keys.settingsData).getOrElse(Nil)
|
||||
else Nil
|
||||
val buildFilter = buildGlobs.toEntryFilter
|
||||
|
||||
/*
|
||||
* This is a callback that will be invoked whenever onEvent returns a Trigger action. The
|
||||
* motivation is to allow the user to specify this callback via setting so that, for example,
|
||||
* they can clear the screen when the build triggers.
|
||||
*/
|
||||
val onTrigger: Event => Watch.Action = {
|
||||
val f: Seq[Event => Unit] = configs.map { params =>
|
||||
val ws = params.watchSettings
|
||||
ws.onTrigger
|
||||
.map(_.apply(params.arguments(logger)))
|
||||
.getOrElse { event: Event =>
|
||||
val globFilter =
|
||||
(params.inputs() ++ params.triggers).toEntryFilter
|
||||
if (globFilter(event.entry)) {
|
||||
ws.triggerMessage match {
|
||||
case Some(Left(tm)) => logger.info(tm(params.watchState(count.get())))
|
||||
case Some(Right(tm)) => tm(count.get(), event).foreach(logger.info(_))
|
||||
case None => // By default don't print anything
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
event: Event =>
|
||||
f.view.foreach(_.apply(event))
|
||||
Watch.Trigger
|
||||
}
|
||||
|
||||
val defaultTrigger = if (Util.isWindows) Watch.ifChanged(Watch.Trigger) else Watch.trigger
|
||||
val onEvent: Event => (Event, Watch.Action) = {
|
||||
val f = configs.map { params =>
|
||||
|
|
@ -504,10 +484,7 @@ object Continuous extends DeprecatedContinuous {
|
|||
).min
|
||||
}
|
||||
event: Event =>
|
||||
event -> (oe(event) match {
|
||||
case Watch.Trigger => onTrigger(event)
|
||||
case a => a
|
||||
})
|
||||
event -> oe(event)
|
||||
}
|
||||
event: Event =>
|
||||
f.view.map(_.apply(event)).minBy(_._2)
|
||||
|
|
@ -568,13 +545,43 @@ object Continuous extends DeprecatedContinuous {
|
|||
quarantinePeriod,
|
||||
retentionPeriod
|
||||
)
|
||||
/*
|
||||
* This is a callback that will be invoked whenever onEvent returns a Trigger action. The
|
||||
* motivation is to allow the user to specify this callback via setting so that, for example,
|
||||
* they can clear the screen when the build triggers.
|
||||
*/
|
||||
val onTrigger: Event => Unit = { event: Event =>
|
||||
configs.foreach { params =>
|
||||
params.watchSettings.onTrigger.foreach(ot => ot(params.arguments(logger))(event))
|
||||
}
|
||||
if (configs.size == 1) {
|
||||
val config = configs.head
|
||||
config.watchSettings.triggerMessage match {
|
||||
case Left(tm) => logger.info(tm(config.watchState(count.get())))
|
||||
case Right(tm) => tm(count.get(), event, commands).foreach(logger.info(_))
|
||||
}
|
||||
} else {
|
||||
Watch.defaultOnTriggerMessage(count.get(), event, commands).foreach(logger.info(_))
|
||||
}
|
||||
}
|
||||
|
||||
(() => {
|
||||
val actions = antiEntropyMonitor.poll(2.milliseconds).map(onEvent)
|
||||
if (actions.exists(_._2 != Watch.Ignore)) {
|
||||
val min = actions.minBy(_._2)
|
||||
logger.debug(s"Received file event actions: ${actions.mkString(", ")}. Returning: $min")
|
||||
min._2
|
||||
} else Watch.Ignore
|
||||
val builder = new StringBuilder
|
||||
val min = actions.minBy {
|
||||
case (e, a) =>
|
||||
if (builder.nonEmpty) builder.append(", ")
|
||||
val path = e.entry.typedPath.toPath.toString
|
||||
builder.append(path)
|
||||
builder.append(" -> ")
|
||||
builder.append(a.toString)
|
||||
a
|
||||
}
|
||||
logger.debug(s"Received file event actions: $builder. Returning: $min")
|
||||
if (min._2 == Watch.Trigger) onTrigger(min._1)
|
||||
Some(min)
|
||||
} else None
|
||||
}, () => monitor.close())
|
||||
}
|
||||
|
||||
|
|
@ -653,21 +660,27 @@ object Continuous extends DeprecatedContinuous {
|
|||
}
|
||||
|
||||
private def combineInputAndFileEvents(
|
||||
nextInputEvent: () => Watch.Action,
|
||||
nextFileEvent: () => Watch.Action,
|
||||
nextInputAction: () => Watch.Action,
|
||||
nextFileEvent: () => Option[(Event, Watch.Action)],
|
||||
logger: Logger
|
||||
): () => Watch.Action = () => {
|
||||
val Seq(inputEvent: Watch.Action, fileEvent: Watch.Action) =
|
||||
Seq(nextInputEvent, nextFileEvent).par.map(_.apply()).toIndexedSeq
|
||||
val min: Watch.Action = Seq[Watch.Action](inputEvent, fileEvent).min
|
||||
val (inputAction: Watch.Action, fileEvent: Option[(Event, Watch.Action)] @unchecked) =
|
||||
Seq(nextInputAction, nextFileEvent).map(_.apply()).toIndexedSeq match {
|
||||
case Seq(ia: Watch.Action, fe @ Some(_)) => (ia, fe)
|
||||
case Seq(ia: Watch.Action, None) => (ia, None)
|
||||
}
|
||||
val min: Watch.Action = (fileEvent.map(_._2).toSeq :+ inputAction).min
|
||||
lazy val inputMessage =
|
||||
s"Received input event: $inputEvent." +
|
||||
(if (inputEvent != min) s" Dropping in favor of file event: $min" else "")
|
||||
lazy val fileMessage =
|
||||
s"Received file event: $fileEvent." +
|
||||
(if (fileEvent != min) s" Dropping in favor of input event: $min" else "")
|
||||
if (inputEvent != Watch.Ignore) logger.debug(inputMessage)
|
||||
if (fileEvent != Watch.Ignore) logger.debug(fileMessage)
|
||||
s"Received input event: $inputAction." +
|
||||
(if (inputAction != min) s" Dropping in favor of file event: $min" else "")
|
||||
if (inputAction != Watch.Ignore) logger.debug(inputMessage)
|
||||
fileEvent
|
||||
.collect {
|
||||
case (event, action) if action != Watch.Ignore =>
|
||||
s"Received file event $action for ${event.entry.typedPath.toPath}." +
|
||||
(if (action != min) s" Dropping in favor of input event: $min" else "")
|
||||
}
|
||||
.foreach(logger.debug(_))
|
||||
min
|
||||
}
|
||||
|
||||
|
|
@ -696,8 +709,7 @@ object Continuous extends DeprecatedContinuous {
|
|||
* @return the wrapped logger.
|
||||
*/
|
||||
private def setLevel(logger: Logger, logLevel: Level.Value, state: State): Logger = {
|
||||
import Level._
|
||||
val delegateLevel = state.get(Keys.logLevel.key).getOrElse(Info)
|
||||
val delegateLevel: Level.Value = state.get(Keys.logLevel.key).getOrElse(Level.Info)
|
||||
/*
|
||||
* The delegate logger may be set to, say, info level, but we want it to print out debug
|
||||
* messages if the logLevel variable above is Debug. To do this, we promote Debug messages
|
||||
|
|
@ -804,7 +816,7 @@ object Continuous extends DeprecatedContinuous {
|
|||
lazy val default = key.get(Keys.watchStartMessage).getOrElse(Watch.defaultStartWatch)
|
||||
key.get(deprecatedWatchingMessage).map(Left(_)).getOrElse(Right(default))
|
||||
}
|
||||
private def getTriggerMessage(key: ScopedKey[_])(implicit e: Extracted): TriggerMessage = Some {
|
||||
private def getTriggerMessage(key: ScopedKey[_])(implicit e: Extracted): TriggerMessage = {
|
||||
lazy val default =
|
||||
key.get(Keys.watchTriggeredMessage).getOrElse(Watch.defaultOnTriggerMessage)
|
||||
key.get(deprecatedWatchingMessage).map(Left(_)).getOrElse(Right(default))
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ import sbt.internal.io.{ WatchState => WS }
|
|||
|
||||
private[internal] trait DeprecatedContinuous {
|
||||
protected type Event = sbt.io.FileEventMonitor.Event[FileAttributes]
|
||||
protected type StartMessage = Option[Either[WS => String, Int => Option[String]]]
|
||||
protected type TriggerMessage = Option[Either[WS => String, (Int, Event) => Option[String]]]
|
||||
protected type StartMessage =
|
||||
Option[Either[WS => String, (Int, String, Seq[String]) => Option[String]]]
|
||||
protected type TriggerMessage = Either[WS => String, (Int, Event, Seq[String]) => Option[String]]
|
||||
protected type DeprecatedWatchState = WS
|
||||
protected val deprecatedWatchingMessage = sbt.Keys.watchingMessage
|
||||
protected val deprecatedTriggeredMessage = sbt.Keys.triggeredMessage
|
||||
|
|
|
|||
|
|
@ -15,17 +15,17 @@ object Build {
|
|||
assert(IO.read(file(stringFile)) == string)
|
||||
}
|
||||
lazy val foo = project.settings(
|
||||
watchStartMessage := { (count: Int) => Some(s"FOO $count") },
|
||||
watchStartMessage := { (count: Int, _, _) => Some(s"FOO $count") },
|
||||
Compile / compile / watchTriggers += baseDirectory.value * "foo.txt",
|
||||
Compile / compile / watchStartMessage := { (count: Int) =>
|
||||
Compile / compile / watchStartMessage := { (count: Int, _, _) =>
|
||||
// this checks that Compile / compile / watchStartMessage
|
||||
// is preferred to Compile / watchStartMessage
|
||||
val outputFile = baseDirectory.value / "foo.txt"
|
||||
IO.write(outputFile, "compile")
|
||||
Some(s"compile $count")
|
||||
},
|
||||
Compile / watchStartMessage := { (count: Int) => Some(s"Compile $count") },
|
||||
Runtime / watchStartMessage := { (count: Int) => Some(s"Runtime $count") },
|
||||
Compile / watchStartMessage := { (count: Int, _, _) => Some(s"Compile $count") },
|
||||
Runtime / watchStartMessage := { (count: Int, _, _) => Some(s"Runtime $count") },
|
||||
setStringValue := {
|
||||
val _ = (fileInputs in (bar, setStringValue)).value
|
||||
setStringValueImpl.evaluated
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ object Build {
|
|||
def setStringValueImpl: Def.Initialize[Task[Unit]] = Def.task {
|
||||
val i = (setStringValue / fileInputs).value
|
||||
val (stringFile, string) = ("foo.txt", "bar")
|
||||
IO.write(file(stringFile), string)
|
||||
val absoluteFile = baseDirectory.value.toPath.resolve(stringFile).toFile
|
||||
IO.write(absoluteFile, string)
|
||||
}
|
||||
def checkStringValueImpl: Def.Initialize[InputTask[Unit]] = Def.inputTask {
|
||||
val Seq(stringFile, string) = Def.spaceDelimited().parsed
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ object Build {
|
|||
val root = (project in file(".")).settings(
|
||||
useSuperShell := false,
|
||||
watchInputStream := inputStream,
|
||||
watchStartMessage := { count =>
|
||||
watchStartMessage := { (_, _, _) =>
|
||||
Build.outputStream.write('\n'.toByte)
|
||||
Build.outputStream.flush()
|
||||
Some("default start message")
|
||||
|
|
@ -24,14 +24,14 @@ object Build {
|
|||
// Note that the order is byeParser | helloParser. In general, we want the higher priority
|
||||
// action to come first because otherwise we would potentially scan past it.
|
||||
val helloOrByeParser: Parser[Watch.Action] = byeParser | helloParser
|
||||
val alternativeStartMessage: Int => Option[String] = { _ =>
|
||||
val alternativeStartMessage: (Int, String, Seq[String]) => Option[String] = { (_, _, _) =>
|
||||
outputStream.write("xybyexyblahxyhelloxy".getBytes)
|
||||
outputStream.flush()
|
||||
Some("alternative start message")
|
||||
}
|
||||
val otherAlternativeStartMessage: Int => Option[String] = { _ =>
|
||||
val otherAlternativeStartMessage: (Int, String, Seq[String]) => Option[String] = { (_, _, _) =>
|
||||
outputStream.write("xyhellobyexyblahx".getBytes)
|
||||
outputStream.flush()
|
||||
Some("other alternative start message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ object Build {
|
|||
setStringValue / watchTriggers += baseDirectory.value * "foo.txt",
|
||||
setStringValue := setStringValueImpl.evaluated,
|
||||
checkStringValue := checkStringValueImpl.evaluated,
|
||||
watchStartMessage := { _ =>
|
||||
watchStartMessage := { (_, _, _) =>
|
||||
IO.touch(baseDirectory.value / "foo.txt", true)
|
||||
Some("watching")
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ object Build {
|
|||
setStringValue / watchTriggers += baseDirectory.value * "foo.txt",
|
||||
setStringValue := setStringValueImpl.evaluated,
|
||||
checkStringValue := checkStringValueImpl.evaluated,
|
||||
watchStartMessage := { _ =>
|
||||
watchStartMessage := { (_, _, _) =>
|
||||
IO.touch(baseDirectory.value / "foo.txt", true)
|
||||
Some("watching")
|
||||
},
|
||||
|
|
@ -31,4 +31,4 @@ object Build {
|
|||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue