add 'project /' and 'project ..' commands

This commit is contained in:
Mark Harrah 2010-10-30 13:24:23 -04:00
parent 730b613007
commit f462cda1fb
4 changed files with 47 additions and 9 deletions

View File

@ -79,9 +79,18 @@ trait Named
{
def name: String
}
trait Navigation[Project]
{
def parentProject: Option[Project]
def self: Project
def initialProject(s: State): Project
def projectClosure(s: State): Seq[Project]
def rootProject: Project
}
trait Member[Node <: Member[Node]]
{ self: Node =>
def projectClosure(state: State): Seq[Node]
{
def navigation: Navigation[Node]
}
trait Tasked
{

View File

@ -53,6 +53,14 @@ ProjectCommand +
""" + ProjectCommand + """ name
Changes to the project with the provided name.
This command fails if there is no project with the given name.
""" + ProjectCommand + """ /
Changes to the initial project.
""" + ProjectCommand + """ ..
Changes to the parent project of the current project.
If there is no parent project, the current project is unchanged.
Use n+1 dots to change to the nth parent.
For example, 'project ....' is equivalent to three consecutive 'project ..' commands.
"""
def projectsBrief = (ProjectsCommand, projectsDetailed)

View File

@ -199,7 +199,7 @@ object Commands
def projects = Command { case s @ State(d: Member[_]) =>
Apply.simple(ProjectsCommand, projectsBrief, projectsDetailed ) { (in,s) =>
val log = logger(s)
d.projectClosure(s).foreach { case n: Named => listProject(n, d eq n, log); case _ => () }
d.navigation.projectClosure(s).foreach { case n: Named => listProject(n, d eq n, log); case _ => () }
s
}(s)
}
@ -212,17 +212,34 @@ object Commands
logger(s).info(d.name)
s
}
else if(to == "/")
setProject(d.navigation.initialProject(s), s)
else if(to.forall(_ == '.'))
if(to.length > 1) gotoParent(to.length - 1, d, s) else s
else
{
d.projectClosure(s).find { case n: Named => n.name == to; case _ => false } match
d.navigation.projectClosure(s).find { case n: Named => n.name == to; case _ => false } match
{
case Some(np) => logger(s).info("Set current project to " + to); s.copy(np)()
case Some(np) => setProject(np, s)
case None => logger(s).error("Invalid project name '" + to + "' (type 'projects' to list available projects)."); s.fail
}
}
}(s)
}
@tailrec def gotoParent[Node <: Member[Node]](n: Int, base: Member[Node], s: State): State =
base.navigation.parentProject match
{
case Some(pp) => if(n <= 1) setProject(pp, s) else gotoParent(n-1, pp, s)
case None => if(s.project == base) s else setProject(base, s)
}
def setProject(np: AnyRef, s: State): State =
{
np match { case n: Named =>
logger(s).info("Set current project to " + n.name)
}
s.copy(np)()
}
def exit = Command { case s => Apply( Help(exitBrief) ) {
case in if TerminateActions contains in.line =>
runExitHooks(s).exit(true)

View File

@ -152,7 +152,12 @@ object MultiContext
val static = (o: Owner, s: String) => context(o).static(o, s)
}
}
final class MultiNavigation(val self: Project, val parentProject: Option[Project]) extends Navigation[Project]
{
def initialProject(s: State): Project = MultiProject.initialProject(s, rootProject)
def projectClosure(s: State): Seq[Project] = MultiProject.topologicalSort(initialProject(s))
@tailrec final lazy val rootProject: Project = parentProject match { case Some(p) => p.navigation.rootProject; case None => self }
}
trait Project extends Tasked with HistoryEnabled with Member[Project] with Named with ConsoleTask with Watched
{
val info: ProjectInfo
@ -165,8 +170,7 @@ trait Project extends Tasked with HistoryEnabled with Member[Project] with Named
def historyPath = Some(outputRootPath / ".history")
def streamBase = outputRootPath / "streams"
def projectClosure(s: State): Seq[Project] = MultiProject.topologicalSort(MultiProject.initialProject(s, rootProject))
@tailrec final def rootProject: Project = info.parent match { case Some(p) => p.rootProject; case None => this }
def navigation: Navigation[Project] = new MultiNavigation(this, info.parent)
implicit def streams = Dummy.Streams
def input = Dummy.In