Add Event trait to FileCacheEntry

Rather than exposing the FileEventMonitor.Event types, which are under
active development in the io repo, I am adding a new event trait to
FileCacheEntry. This trait doesn't expose any internal implementation
details.
This commit is contained in:
Ethan Atkins 2019-03-15 11:09:29 -07:00
parent 86200345e1
commit be94b25d68
4 changed files with 50 additions and 22 deletions

View File

@ -23,7 +23,6 @@ import sbt.internal.util.Types.const
import sbt.internal.util.complete.{ DefaultParsers, Parser }
import sbt.internal.util.{ AttributeKey, JLine }
import sbt.internal.{ FileCacheEntry, LegacyWatched }
import sbt.io.FileEventMonitor.{ Creation, Deletion, Event, Update }
import sbt.io._
import sbt.util.{ Level, Logger }
@ -145,13 +144,14 @@ object Watched {
private[sbt] def onEvent(
sources: Seq[WatchSource],
projectSources: Seq[WatchSource]
): Event[FileCacheEntry] => Watched.Action =
): FileCacheEntry.Event => Watched.Action =
event =>
if (sources.exists(_.accept(event.entry.typedPath.toPath))) Watched.Trigger
else if (projectSources.exists(_.accept(event.entry.typedPath.toPath))) event match {
case Update(prev, cur, _) if prev.value != cur.value => Reload
case _: Creation[_] | _: Deletion[_] => Reload
case _ => Ignore
if (sources.exists(_.accept(event.path))) Watched.Trigger
else if (projectSources.exists(_.accept(event.path))) {
(event.previous, event.current) match {
case (Some(p), Some(c)) => if (c == p) Watched.Ignore else Watched.Reload
case _ => Watched.Trigger
}
} else Ignore
private[this] val reRun = if (isWin) "" else " or 'r' to re-run the command"
@ -333,7 +333,9 @@ object Watched {
case action @ (CancelWatch | HandleError | Reload | _: Custom) => action
case Trigger => Trigger
case _ =>
val events = config.fileEventMonitor.poll(10.millis)
val events = config.fileEventMonitor
.poll(10.millis)
.map(new FileCacheEntry.EventImpl(_))
val next = events match {
case Seq() => (Ignore, None)
case Seq(head, tail @ _*) =>
@ -362,14 +364,14 @@ object Watched {
if (action == HandleError) "error"
else if (action.isInstanceOf[Custom]) action.toString
else "cancellation"
logger.debug(s"Stopping watch due to $cause from ${event.entry.typedPath.toPath}")
logger.debug(s"Stopping watch due to $cause from ${event.path}")
action
case (Trigger, Some(event)) =>
logger.debug(s"Triggered by ${event.entry.typedPath.toPath}")
config.triggeredMessage(event.entry.typedPath.toPath, count).foreach(info)
logger.debug(s"Triggered by ${event.path}")
config.triggeredMessage(event.path, count).foreach(info)
Trigger
case (Reload, Some(event)) =>
logger.info(s"Reload triggered by ${event.entry.typedPath.toPath}")
logger.info(s"Reload triggered by ${event.path}")
Reload
case _ =>
nextAction()
@ -481,7 +483,7 @@ trait WatchConfig {
* @param event the detected sbt.io.FileEventMonitor.Event.
* @return the next [[Watched.Action Action]] to run.
*/
def onWatchEvent(event: Event[FileCacheEntry]): Watched.Action
def onWatchEvent(event: FileCacheEntry.Event): Watched.Action
/**
* Transforms the state after the watch terminates.
@ -540,7 +542,7 @@ object WatchConfig {
fileEventMonitor: FileEventMonitor[FileCacheEntry],
handleInput: InputStream => Watched.Action,
preWatch: (Int, Boolean) => Watched.Action,
onWatchEvent: Event[FileCacheEntry] => Watched.Action,
onWatchEvent: FileCacheEntry.Event => Watched.Action,
onWatchTerminated: (Watched.Action, String, State) => State,
triggeredMessage: (Path, Int) => Option[String],
watchingMessage: Int => Option[String]
@ -559,7 +561,7 @@ object WatchConfig {
override def handleInput(inputStream: InputStream): Watched.Action = hi(inputStream)
override def preWatch(count: Int, lastResult: Boolean): Watched.Action =
pw(count, lastResult)
override def onWatchEvent(event: Event[FileCacheEntry]): Watched.Action = owe(event)
override def onWatchEvent(event: FileCacheEntry.Event): Watched.Action = owe(event)
override def onWatchTerminated(action: Watched.Action, command: String, state: State): State =
owt(action, command, state)
override def triggeredMessage(path: Path, count: Int): Option[String] =

View File

@ -8,10 +8,12 @@
package sbt
package internal
import java.lang
import java.nio.file.Path
import java.util.Optional
import sbt.internal.inc.{ EmptyStamp, LastModified, Stamp }
import sbt.io.TypedPath
import sbt.io.FileEventMonitor.{ Creation, Deletion, Update }
import sbt.io.{ FileEventMonitor, TypedPath }
import xsbti.compile.analysis.{ Stamp => XStamp }
/**
@ -23,7 +25,33 @@ trait FileCacheEntry {
def lastModified: Option[Long]
}
object FileCacheEntry {
def default(typedPath: TypedPath): FileCacheEntry =
trait Event {
def path: Path
def previous: Option[FileCacheEntry]
def current: Option[FileCacheEntry]
}
private[sbt] class EventImpl(event: FileEventMonitor.Event[FileCacheEntry]) extends Event {
override def path: Path = event.entry.typedPath.toPath
override def previous: Option[FileCacheEntry] = event match {
case Deletion(entry, _) => entry.value.toOption
case Update(previous, _, _) => previous.value.toOption
case _ => None
}
override def current: Option[FileCacheEntry] = event match {
case Creation(entry, _) => entry.value.toOption
case Update(_, current, _) => current.value.toOption
case _ => None
}
override def equals(o: Any): Boolean = o match {
case that: Event =>
this.path == that.path && this.previous == that.previous && this.current == that.current
case _ => false
}
override def hashCode(): Int =
((path.hashCode * 31) ^ previous.hashCode() * 31) ^ current.hashCode()
override def toString: String = s"Event($path, $previous, $current)"
}
private[sbt] def default(typedPath: TypedPath): FileCacheEntry =
DelegateFileCacheEntry(Stamped.converter(typedPath))
private[sbt] implicit class FileCacheEntryOps(val e: FileCacheEntry) extends AnyVal {
private[sbt] def stamp: XStamp = e match {

View File

@ -15,7 +15,6 @@ import org.scalatest.{ FlatSpec, Matchers }
import sbt.Watched._
import sbt.WatchedSpec._
import sbt.internal.FileCacheEntry
import sbt.io.FileEventMonitor.Event
import sbt.io._
import sbt.io.syntax._
import sbt.util.Logger
@ -31,7 +30,7 @@ class WatchedSpec extends FlatSpec with Matchers {
logger: Logger = NullLogger,
handleInput: InputStream => Action = _ => Ignore,
preWatch: (Int, Boolean) => Action = (_, _) => CancelWatch,
onWatchEvent: Event[FileCacheEntry] => Action = _ => Ignore,
onWatchEvent: FileCacheEntry.Event => Action = _ => Ignore,
triggeredMessage: (Path, Int) => Option[String] = (_, _) => None,
watchingMessage: Int => Option[String] = _ => None
): WatchConfig = {
@ -87,7 +86,7 @@ class WatchedSpec extends FlatSpec with Matchers {
val config = Defaults.config(
globs = Seq(realDir ** AllPassFilter),
preWatch = (count, _) => if (count == 2) CancelWatch else Ignore,
onWatchEvent = e => if (e.entry.typedPath.toPath == foo) Trigger else Ignore,
onWatchEvent = e => if (e.path == foo) Trigger else Ignore,
triggeredMessage = (tp, _) => { queue += tp; None },
watchingMessage = _ => { Files.createFile(bar); Thread.sleep(5); Files.createFile(foo); None }
)

View File

@ -23,7 +23,6 @@ import sbt.internal.io.WatchState
import sbt.internal.librarymanagement.{ CompatibilityWarningOptions, IvySbt }
import sbt.internal.server.ServerHandler
import sbt.internal.util.{ AttributeKey, SourcePosition }
import sbt.io.FileEventMonitor.Event
import sbt.io._
import sbt.librarymanagement.Configurations.CompilerPlugin
import sbt.librarymanagement.LibraryManagementCodec._
@ -102,7 +101,7 @@ object Keys {
val watchConfig = taskKey[WatchConfig]("The configuration for continuous execution.").withRank(BMinusSetting)
val watchLogger = taskKey[Logger]("A logger that reports watch events.").withRank(DSetting)
val watchHandleInput = settingKey[InputStream => Watched.Action]("Function that is periodically invoked to determine if the continous build should be stopped or if a build should be triggered. It will usually read from stdin to respond to user commands.").withRank(BMinusSetting)
val watchOnEvent = taskKey[Event[FileCacheEntry] => Watched.Action]("Determines how to handle a file event").withRank(BMinusSetting)
val watchOnEvent = taskKey[FileCacheEntry.Event => Watched.Action]("Determines how to handle a file event").withRank(BMinusSetting)
val watchOnTermination = taskKey[(Watched.Action, String, State) => State]("Transforms the input state after the continuous build completes.").withRank(BMinusSetting)
val watchService = settingKey[() => WatchService]("Service to use to monitor file system changes.").withRank(BMinusSetting)
val watchProjectSources = taskKey[Seq[Watched.WatchSource]]("Defines the sources for the sbt meta project to watch to trigger a reload.").withRank(CSetting)