continuous polling interval now in milliseconds

This commit is contained in:
Mark Harrah 2011-03-01 08:51:14 -05:00
parent 3922580c58
commit 11c2b2239c
4 changed files with 22 additions and 26 deletions

View File

@ -11,7 +11,7 @@ package sbt
import sbt.complete.{DefaultParsers, Parser}
import Command.applyEffect
import Keys.{Analysis,HistoryPath,Logged,ShellPrompt,Watch}
import Keys.{Analysis,HistoryPath,Logged,ShellPrompt}
import scala.annotation.tailrec
import scala.collection.JavaConversions._
import Function.tupled
@ -197,7 +197,7 @@ object BuiltinCommands
// TODO: nest
def continuous =
Command.single(ContinuousExecutePrefix, Help(continuousBriefHelp) ) { (s, arg) =>
withAttribute(s, Watch.key, "Continuous execution not configured.") { w =>
withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w =>
val repeat = ContinuousExecutePrefix + (if(arg.startsWith(" ")) arg else " " + arg)
Watched.executeContinuously(w, s, arg, repeat)
}

View File

@ -75,13 +75,14 @@ object Project extends Init[Scope]
val ref = ProjectRef(uri, id)
val project = Load.getProject(structure.units, uri, id)
logger(s).info("Set current project to " + id + " (in build " + uri +")")
def get[T](k: SettingKey[T]): Option[T] = k in ref get structure.data
val data = structure.data
val historyPath = HistoryPath in ref get data flatMap identity
val prompt = ShellPrompt in ref get data
val commands = (Commands in ref get data).toList.flatten[Command].map(_ tag (ProjectCommand, true))
val historyPath = get(HistoryPath) flatMap identity
val prompt = get(ShellPrompt)
val watched = get(Watch)
val commands = get(Commands).toList.flatten[Command].map(_ tag (ProjectCommand, true))
val newProcessors = commands ++ BuiltinCommands.removeTagged(s.processors, ProjectCommand)
val newAttrs = s.attributes.put(Watch.key, makeWatched(data, ref, project)).put(HistoryPath.key, historyPath)
val newAttrs = setCond(Watched.Configuration, watched, s.attributes).put(HistoryPath.key, historyPath)
s.copy(attributes = setCond(ShellPrompt.key, prompt, newAttrs), processors = newProcessors)
}
def setCond[T](key: AttributeKey[T], vopt: Option[T], attributes: AttributeMap): AttributeMap =
@ -89,17 +90,6 @@ object Project extends Init[Scope]
def makeSettings(settings: Seq[Setting[_]], delegates: Scope => Seq[Scope], scopeLocal: ScopedKey[_] => Seq[Setting[_]]) =
translateUninitialized( make(settings)(delegates, scopeLocal) )
def makeWatched(data: Settings[Scope], ref: ProjectRef, project: Project): Watched =
{
def getWatch(ref: ProjectRef) = Watch in ref get data
getWatch(ref) match
{
case Some(currentWatch) =>
val subWatches = project.uses flatMap { p => getWatch(p) }
Watched.multi(currentWatch, subWatches)
case None => Watched.empty
}
}
def display(scoped: ScopedKey[_]): String = Scope.display(scoped.scope, scoped.key.label)
def display(ref: ProjectRef): String = "(" + (ref.uri map (_.toString) getOrElse "<this>") + ")" + (ref.id getOrElse "<root>")

View File

@ -5,13 +5,17 @@ package sbt
import CommandSupport.{ClearOnFailure,FailureWall}
import annotation.tailrec
import java.io.File
trait Watched
{
/** A `PathFinder` that determines the files watched when an action is run with a preceeding ~ when this is the current
* project. This project does not need to include the watched paths for projects that this project depends on.*/
def watchPaths: PathFinder = Path.emptyPathFinder
def watchPaths(s: State): Seq[File] = Nil
def terminateWatch(key: Int): Boolean = Watched.isEnter(key)
/** The time in milliseconds between checking for changes. The actual time between the last change made to a file and the
* execution time is between `pollInterval` and `pollInterval*2`.*/
def pollInterval: Int = Watched.PollDelayMillis
}
object Watched
@ -21,24 +25,25 @@ object Watched
def multi(base: Watched, paths: Seq[Watched]): Watched =
new AWatched
{
override val watchPaths = (base.watchPaths /: paths)(_ +++ _.watchPaths)
override def watchPaths(s: State) = (base.watchPaths(s) /: paths)(_ ++ _.watchPaths(s))
override def terminateWatch(key: Int): Boolean = base.terminateWatch(key)
override val pollInterval = (base +: paths).map(_.pollInterval).min
}
def empty: Watched = new AWatched
val PollDelaySeconds = 1
val PollDelayMillis = 500
def isEnter(key: Int): Boolean = key == 10 || key == 13
def executeContinuously(watched: Watched, s: State, next: String, repeat: String): State =
{
@tailrec def shouldTerminate: Boolean = (System.in.available > 0) && (watched.terminateWatch(System.in.read()) || shouldTerminate)
val sourcesFinder = watched.watchPaths
val sourcesFinder = Path.finder { watched watchPaths s }
val watchState = s get ContinuousState getOrElse WatchState.empty
if(watchState.count > 0)
System.out.println(watchState.count + ". Waiting for source changes... (press enter to interrupt)")
val (triggered, newWatchState) = SourceModificationWatch.watch(sourcesFinder, PollDelaySeconds, watchState)(shouldTerminate)
val (triggered, newWatchState) = SourceModificationWatch.watch(sourcesFinder, watched.pollInterval, watchState)(shouldTerminate)
if(triggered)
(ClearOnFailure :: next :: FailureWall :: repeat :: s).put(ContinuousState, newWatchState)
@ -49,4 +54,5 @@ object Watched
}
}
val ContinuousState = AttributeKey[WatchState]("watch state")
val Configuration = AttributeKey[Watched]("watched-configuration")
}

View File

@ -7,7 +7,7 @@ package sbt
object SourceModificationWatch
{
@tailrec def watch(sourcesFinder: PathFinder, pollDelaySec: Int, state: WatchState)(terminationCondition: => Boolean): (Boolean, WatchState) =
@tailrec def watch(sourcesFinder: PathFinder, pollDelayMillis: Int, state: WatchState)(terminationCondition: => Boolean): (Boolean, WatchState) =
{
import state._
@ -30,11 +30,11 @@ object SourceModificationWatch
(true, newState)
else
{
Thread.sleep(pollDelaySec * 1000)
Thread.sleep(pollDelayMillis)
if(terminationCondition)
(false, newState)
else
watch(sourcesFinder, pollDelaySec, newState)(terminationCondition)
watch(sourcesFinder, pollDelayMillis, newState)(terminationCondition)
}
}
}